From 0d05b1868227a28f168b07ca0f3478001314ff6c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 22 Aug 2017 00:30:17 -0400 Subject: [PATCH 001/414] Add startFrame variable for tracking length of frames --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index fc2d553..3cb32c9 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -5,6 +5,7 @@ const gpio = require('gpio') class Intval { constructor () { this._pin = {} + this._startFrame = 0 this._declarePins() } _declarePins () { From 9902702f84088f0d9e919b8a07ecaf4d29169be4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 22 Aug 2017 00:38:53 -0400 Subject: [PATCH 002/414] Caught misnamed files from original project --- scripts/{blootstrap-deps.sh => intval3-deps.sh} | 4 ++-- .../{blootstrap-install.sh => intval3-install.sh} | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) rename scripts/{blootstrap-deps.sh => intval3-deps.sh} (77%) rename scripts/{blootstrap-install.sh => intval3-install.sh} (65%) diff --git a/scripts/blootstrap-deps.sh b/scripts/intval3-deps.sh similarity index 77% rename from scripts/blootstrap-deps.sh rename to scripts/intval3-deps.sh index 90a92f3..3b1c923 100644 --- a/scripts/blootstrap-deps.sh +++ b/scripts/intval3-deps.sh @@ -1,6 +1,6 @@ #!/bin/bash -echo "Running blootstrap install script" +echo "Running intval3 install script" apt-get update apt-get install git ufw nginx -y @@ -14,4 +14,4 @@ npm install -g pm2 echo "Installing bluetooth dependencies..." apt-get install bluetooth bluez libbluetooth-dev libudev-dev -y -echo "Finished installing blootstrap dependencies" \ No newline at end of file +echo "Finished installing intval3 dependencies" \ No newline at end of file diff --git a/scripts/blootstrap-install.sh b/scripts/intval3-install.sh similarity index 65% rename from scripts/blootstrap-install.sh rename to scripts/intval3-install.sh index 8e3258b..ae311a6 100644 --- a/scripts/blootstrap-install.sh +++ b/scripts/intval3-install.sh @@ -1,6 +1,6 @@ #!/bin/bash -echo "Running blootstrap install script" +echo "Running intval3 install script" apt-get update apt-get install git ufw nginx -y @@ -13,6 +13,8 @@ npm install -g pm2 echo "Installing bluetooth dependencies..." apt-get install bluetooth bluez libbluetooth-dev libudev-dev -y +systemctl disable bluetooth +hciconfig hci0 up echo "Configuring ufw (firewall)..." ufw default deny incoming @@ -22,13 +24,13 @@ ufw allow http ufw allow https ufw enable -echo "Installing blootstrap project..." -wget https://github.com/mattmcw/blootstrap/archive/master.zip -unzip master.zip -d blootstrap/ +echo "Installing intval3 project..." +wget https://github.com/sixteenmillimeter/intval3/archive/master.zip +unzip master.zip -d intval3/ rm master.zip -cd blootstrap +cd intval3 npm install pm2 start process.json -echo "Finished installing blootstrap" \ No newline at end of file +echo "Finished installing intval3" \ No newline at end of file From 77a10fa80a19a844c3ba3584d3a5958de3db629c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 22 Aug 2017 00:49:08 -0400 Subject: [PATCH 003/414] Add stubb functionality for starting and stopping motor via hbridge --- lib/intval/index.js | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 3cb32c9..57f9c82 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -9,13 +9,46 @@ class Intval { this._declarePins() } _declarePins () { - this._pin.four = gpio.export(4, { + this._pin.fwd = gpio.export(4, { direction: 'out', interval: 100, ready : () => { console.info(`Set pin 4 to OUTPUT`) } }) + this._pin.bwd = gpio.export(5, { + direction: 'out', + interval: 100, + ready : () => { + console.info(`Set pin 5 to OUTPUT`) + } + }) + } + _startFwd () { + this._pin.fwd.set(1) + this._pin.bwd.set(0) + } + _startBwd () { + this._pin.fwd.set(0) + this._pin.bwd.set(1) + } + frame (dir = true, time = 0, delay = 0) { + if (delay !== 0) { + setTimeout(function () { + if (dir) { + this._startFwd() + } else { + this._startBwd() + } + + }, delay) + } else { + if (dir) { + this._startFwd() + } else { + this._startBwd() + } + } } status () { return {} From 524aba90d81d0938e7f1e005085e12f61d986829 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 22 Aug 2017 01:31:27 -0400 Subject: [PATCH 004/414] Install onoff for better button watching --- lib/intval/index.js | 40 +++++++++++++++++++++++++--------------- package-lock.json | 25 +++++++++++++++++++++++-- package.json | 1 + 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 57f9c82..05d3a19 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,6 +1,26 @@ 'use strict' -const gpio = require('gpio') +const Gpio = require('onoff') +const PINS = { + fwd : { + pin : 4, + dir : 'out' + }, + bwd : { + pin : 5, + dir : 'out' + }, + micro : { + pin : 6, + dir : 'in', + edge : 'rising' + }, + release : { + pin : 7, + dir : 'in', + edge : 'both' + } +} class Intval { constructor () { @@ -9,20 +29,10 @@ class Intval { this._declarePins() } _declarePins () { - this._pin.fwd = gpio.export(4, { - direction: 'out', - interval: 100, - ready : () => { - console.info(`Set pin 4 to OUTPUT`) - } - }) - this._pin.bwd = gpio.export(5, { - direction: 'out', - interval: 100, - ready : () => { - console.info(`Set pin 5 to OUTPUT`) - } - }) + this._pin.fwd = Gpio(4, 'out') + this._pin.bwd = Gpio(5, 'out') + this._pin.micro = Gpio(6, 'in', 'rising') + this._pin.release = Gpio(7, 'in', 'both') } _startFwd () { this._pin.fwd.set(1) diff --git a/package-lock.json b/package-lock.json index b93b75d..e401bc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,11 @@ "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" } }, + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" + }, "bleno": { "version": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", "integrity": "sha1-IesK10O850eU45L0ph4TsHOT26o=", @@ -161,6 +166,15 @@ "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" } }, + "epoll": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/epoll/-/epoll-0.1.22.tgz", + "integrity": "sha1-GrmmN8/xGiCXDIiLHKIXi2TPUpE=", + "requires": { + "bindings": "1.2.1", + "nan": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + } + }, "escape-regexp-component": { "version": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" @@ -371,8 +385,7 @@ }, "nan": { "version": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", - "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", - "optional": true + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" }, "ncp": { "version": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", @@ -403,6 +416,14 @@ "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } }, + "onoff": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/onoff/-/onoff-1.1.5.tgz", + "integrity": "sha1-tkT9pSe4eJ60ZDwD9YIqqVclCu4=", + "requires": { + "epoll": "0.1.22" + } + }, "path-is-absolute": { "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", diff --git a/package.json b/package.json index 8b065f7..3389645 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "cron": "^1.2.1", "gpio": "^0.2.7", "node-ipc": "^9.1.0", + "onoff": "^1.1.5", "restify": "^5.2.0", "uuid": "^3.1.0", "winston": "^2.3.1" From 90210e02f3cf1d4f9062677d15b62127b6b104c1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 26 Aug 2017 19:06:43 -0400 Subject: [PATCH 005/414] Dump contents of the onoff to console --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index 05d3a19..2855880 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,6 +1,7 @@ 'use strict' const Gpio = require('onoff') +console.dir(Gpio) const PINS = { fwd : { pin : 4, From 760c6c72aba2bdd100dead82fdd1731a80eaea8e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 26 Aug 2017 19:08:39 -0400 Subject: [PATCH 006/414] Require specifically the Gpio function --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 2855880..9dbc72a 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,6 +1,6 @@ 'use strict' -const Gpio = require('onoff') +const Gpio = require('onoff').Gpio console.dir(Gpio) const PINS = { fwd : { From 40cfda8d9012ebb0433663bf3a9c7e920407d97c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 26 Aug 2017 19:26:30 -0400 Subject: [PATCH 007/414] Remove logging. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 9dbc72a..bd96a15 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,7 +1,7 @@ 'use strict' const Gpio = require('onoff').Gpio -console.dir(Gpio) + const PINS = { fwd : { pin : 4, From 9eac1e0193d6cc0ca5c111fb52b16565a7db515b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 28 Aug 2017 08:49:47 -0400 Subject: [PATCH 008/414] Install with latest node.js and latest npm Create a sim library as a fallback for the on off module, for development --- index.js | 17 +- lib/intval/index.js | 17 +- lib/onoffsim/index.js | 25 ++ package-lock.json | 531 ++++++++++++++++++++---------------- package.json | 2 +- services/bluetooth/index.js | 1 + tests/index.js | 3 + 7 files changed, 360 insertions(+), 236 deletions(-) create mode 100644 lib/onoffsim/index.js create mode 100644 tests/index.js diff --git a/index.js b/index.js index f1c5f69..77d6ca8 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,16 @@ 'use strict' -const ble = require('./lib/blootstrap') -const intval = require('./lib/intval') const restify = require('restify') const logger = require('winston') const fs = require('fs') -const pin = {} + +const ble = require('./lib/blootstrap') +const intval = require('./lib/intval') const PACKAGE = require('./package.json') const PORT = process.env.PORT || 6699 const APPNAME = PACKAGE.name -const INDEX = fs.readFileSync('./app/www/index.html', 'utf8') +const INDEXPATH = './app/www/index.html' let app = restify.createServer({ name: APPNAME, @@ -41,8 +41,13 @@ function rStatus (req, res, next) { } function index (req, res, next) { - res.end(INDEX) - return next() + fs.readFile(INDEXPATH, (err, data) => { + if (err) { + return next(err) + } + res.end(data) + next() + }, 'utf8') } diff --git a/lib/intval/index.js b/lib/intval/index.js index bd96a15..3b291fb 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,6 +1,13 @@ 'use strict' -const Gpio = require('onoff').Gpio +let Gpio +try { + Gpio = require('onoff').Gpio +} catch (e) { + console.warn('Failed including Gpio, using sim') + Gpio = require('../../lib/onoffsim').Gpio +} + const PINS = { fwd : { @@ -34,15 +41,23 @@ class Intval { this._pin.bwd = Gpio(5, 'out') this._pin.micro = Gpio(6, 'in', 'rising') this._pin.release = Gpio(7, 'in', 'both') + + this._pin.release.watch(this._watchRelease) } _startFwd () { this._pin.fwd.set(1) this._pin.bwd.set(0) + //start high-cpu watch } _startBwd () { this._pin.fwd.set(0) this._pin.bwd.set(1) } + _watchRelease (err, val) { + if (err) { + console.error(err); + } + } frame (dir = true, time = 0, delay = 0) { if (delay !== 0) { setTimeout(function () { diff --git a/lib/onoffsim/index.js b/lib/onoffsim/index.js new file mode 100644 index 0000000..b45bb01 --- /dev/null +++ b/lib/onoffsim/index.js @@ -0,0 +1,25 @@ +'use strict' + +const onoffsim = { + Gpio : function (no, dir = 'in', additional = 'none') { + // + return { + no : no, + dir : dir, + additional : additional, + val : null, + watchFunc : null, + set : function (val) { + console.log(`onoffsim set ${this.no} to ${val}`) + }, + get : function () { + return this.val + }, + watch : function (cb) { + this.watchFunc = cb + } + } + } +} + +module.exports = onoffsim \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e401bc6..9335397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,11 +5,13 @@ "requires": true, "dependencies": { "asn1": { - "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "async": { @@ -18,16 +20,18 @@ "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" }, "balanced-match": { - "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, "bcrypt-pbkdf": { - "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + "tweetnacl": "0.14.5" } }, "bindings": { @@ -36,44 +40,49 @@ "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" }, "bleno": { - "version": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", "integrity": "sha1-IesK10O850eU45L0ph4TsHOT26o=", "requires": { - "bplist-parser": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "xpc-connection": "https://registry.npmjs.org/xpc-connection/-/xpc-connection-0.1.4.tgz" + "bplist-parser": "0.0.6", + "debug": "2.6.8", + "xpc-connection": "0.1.4" } }, "bplist-parser": { - "version": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", "integrity": "sha1-ONo0cYF9+dRKs4kuJ3B7u9daEbk=", "optional": true }, "brace-expansion": { - "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "optional": true, "requires": { - "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "balanced-match": "1.0.0", + "concat-map": "0.0.1" } }, "bunyan": { - "version": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "requires": { - "dtrace-provider": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", - "moment": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "mv": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "safe-json-stringify": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" + "dtrace-provider": "0.8.5", + "moment": "2.18.1", + "mv": "2.1.1", + "safe-json-stringify": "1.0.4" } }, "clone-regexp": { - "version": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.0.tgz", "integrity": "sha1-6uCiQT9VwJQvgYwin+/OhF1/Oxw=", "requires": { - "is-regexp": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "is-supported-regexp-flag": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz" + "is-regexp": "1.0.0", + "is-supported-regexp-flag": "1.0.0" } }, "colors": { @@ -82,12 +91,14 @@ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" }, "concat-map": { - "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, "core-util-is": { - "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cron": { @@ -99,28 +110,32 @@ } }, "csv": { - "version": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", "integrity": "sha1-2ZUtWbH5ZKevvN2ATWgYpzGZpHc=", "requires": { - "csv-generate": "https://registry.npmjs.org/csv-generate/-/csv-generate-1.0.0.tgz", - "csv-parse": "https://registry.npmjs.org/csv-parse/-/csv-parse-1.2.1.tgz", - "csv-stringify": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.0.4.tgz", - "stream-transform": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz" + "csv-generate": "1.0.0", + "csv-parse": "1.2.1", + "csv-stringify": "1.0.4", + "stream-transform": "0.1.2" } }, "csv-generate": { - "version": "https://registry.npmjs.org/csv-generate/-/csv-generate-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-1.0.0.tgz", "integrity": "sha1-vVKIaFnQySXz5R9g86vtJi+hXK8=" }, "csv-parse": { - "version": "https://registry.npmjs.org/csv-parse/-/csv-parse-1.2.1.tgz", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-1.2.1.tgz", "integrity": "sha1-kZnCPySQ2YxNmrKgFnsGknSYyd8=" }, "csv-stringify": { - "version": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.0.4.tgz", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.0.4.tgz", "integrity": "sha1-vBi6ua1M7zGV/SV5gLWLR5xC0+U=", "requires": { - "lodash.get": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" + "lodash.get": "4.4.2" } }, "cycle": { @@ -129,41 +144,47 @@ "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" }, "dashdash": { - "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "assert-plus": "1.0.0" } }, "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "ms": "2.0.0" } }, "detect-node": { - "version": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, "dtrace-provider": { - "version": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", "integrity": "sha1-mOu6Ihr6xG4cOf02hY2Pk2dSS5I=", "optional": true, "requires": { - "nan": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + "nan": "2.6.2" } }, "easy-stack": { - "version": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=" }, "ecc-jsbn": { - "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + "jsbn": "0.1.1" } }, "epoll": { @@ -172,19 +193,22 @@ "integrity": "sha1-GrmmN8/xGiCXDIiLHKIXi2TPUpE=", "requires": { "bindings": "1.2.1", - "nan": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + "nan": "2.6.2" } }, "escape-regexp-component": { - "version": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" }, "event-pubsub": { - "version": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.2.4.tgz", - "integrity": "sha1-Y0+2l/5AFNxQBHIthZQyuqISMlc=" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" }, "extsprintf": { - "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "eyes": { @@ -193,88 +217,95 @@ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" }, "formidable": { - "version": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=" }, "getpass": { - "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "assert-plus": "1.0.0" } }, "glob": { - "version": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "optional": true, "requires": { - "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "gpio": { - "version": "https://registry.npmjs.org/gpio/-/gpio-0.2.7.tgz", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/gpio/-/gpio-0.2.7.tgz", "integrity": "sha1-vzhsiJYe/QpPag/3FnrA/zmX6rk=" }, "handle-thing": { - "version": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=" }, "hpack.js": { - "version": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "obuf": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "wbuf": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz" + "inherits": "2.0.3", + "obuf": "1.1.1", + "readable-stream": "2.3.3", + "wbuf": "1.7.2" } }, "http-deceiver": { - "version": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" }, "http-signature": { - "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" - }, - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" - } + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" } }, "inflight": { - "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { - "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "is-regexp": { - "version": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" }, "is-supported-regexp-flag": { - "version": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz", "integrity": "sha1-i1IMhfrnolM4LUsCZS4EVXbhO7g=" }, "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isstream": { @@ -283,82 +314,96 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "js-message": { - "version": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=" }, "js-queue": { - "version": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", "requires": { - "easy-stack": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz" + "easy-stack": "1.0.0" } }, "jsbn": { - "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, "json-schema": { - "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "jsprim": { - "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "json-schema": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" } }, "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, "lodash.get": { - "version": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lru-cache": { - "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "requires": { - "pseudomap": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "yallist": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "mime": { - "version": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" }, "minimalistic-assert": { - "version": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" }, "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "optional": true, "requires": { - "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + "brace-expansion": "1.1.8" } }, "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "optional": true }, "mkdirp": { - "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "optional": true, "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "minimist": "0.0.8" } }, "moment": { - "version": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" }, "moment-timezone": { @@ -366,198 +411,215 @@ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.13.tgz", "integrity": "sha1-mc5cfYJyYusPH3AgRBd/YHRde5A=", "requires": { - "moment": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" + "moment": "2.18.1" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mv": { - "version": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", "optional": true, "requires": { - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "ncp": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz" + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" } }, "nan": { - "version": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" }, "ncp": { - "version": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, "negotiator": { - "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, "node-ipc": { - "version": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.0.tgz", - "integrity": "sha1-rZd4NmcWX1lE6kHPBmGPHEvheSo=", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", "requires": { - "event-pubsub": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.2.4.tgz", - "js-message": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", - "js-queue": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz" + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" } }, "obuf": { - "version": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", "integrity": "sha1-EEEktsYCxnlogaBCVB0220OlJk4=" }, "once": { - "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "wrappy": "1.0.2" } }, "onoff": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/onoff/-/onoff-1.1.5.tgz", - "integrity": "sha1-tkT9pSe4eJ60ZDwD9YIqqVclCu4=", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/onoff/-/onoff-1.1.7.tgz", + "integrity": "sha1-kZKHlby16QL+dVG9e7JVHZunZII=", "requires": { "epoll": "0.1.22" } }, "path-is-absolute": { - "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { - "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "pseudomap": { - "version": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "qs": { - "version": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", - "integrity": "sha1-jQSVTTZN7z78VbWgeT4eLIsebkk=" + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" }, "readable-stream": { - "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "restify": { - "version": "https://registry.npmjs.org/restify/-/restify-5.2.0.tgz", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/restify/-/restify-5.2.0.tgz", "integrity": "sha1-9xMxAu85WKVtpC92BBURKnigGCI=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "bunyan": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", - "clone-regexp": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.0.tgz", - "csv": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", - "dtrace-provider": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", - "escape-regexp-component": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", - "formidable": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", - "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "mime": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", - "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "qs": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", - "restify-errors": "https://registry.npmjs.org/restify-errors/-/restify-errors-4.3.0.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "spdy": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "vasync": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", - "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - }, - "dependencies": { - "uuid": { - "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" - } + "assert-plus": "1.0.0", + "bunyan": "1.8.12", + "clone-regexp": "1.0.0", + "csv": "1.1.1", + "dtrace-provider": "0.8.5", + "escape-regexp-component": "1.0.2", + "formidable": "1.1.1", + "http-signature": "1.2.0", + "lodash": "4.17.4", + "lru-cache": "4.1.1", + "mime": "1.3.6", + "negotiator": "0.6.1", + "once": "1.4.0", + "qs": "6.5.0", + "restify-errors": "4.3.0", + "semver": "5.4.1", + "spdy": "3.4.7", + "uuid": "3.1.0", + "vasync": "1.6.4", + "verror": "1.10.0" } }, "restify-errors": { - "version": "https://registry.npmjs.org/restify-errors/-/restify-errors-4.3.0.tgz", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-4.3.0.tgz", "integrity": "sha1-7JDzCTTX8xGRNRgd/DA+ML5gGr4=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "safe-json-stringify": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", - "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" + "assert-plus": "1.0.0", + "lodash": "4.17.4", + "safe-json-stringify": "1.0.4", + "verror": "1.10.0" } }, "rimraf": { - "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "optional": true, "requires": { - "glob": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" + "glob": "6.0.4" } }, "safe-buffer": { - "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safe-json-stringify": { - "version": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", "optional": true }, "select-hose": { - "version": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" }, "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=" + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "spdy": { - "version": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "handle-thing": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "http-deceiver": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "select-hose": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "spdy-transport": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz" + "debug": "2.6.8", + "handle-thing": "1.2.5", + "http-deceiver": "1.2.7", + "safe-buffer": "5.1.1", + "select-hose": "2.0.0", + "spdy-transport": "2.0.20" } }, "spdy-transport": { - "version": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "detect-node": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", - "hpack.js": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "obuf": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "wbuf": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz" + "debug": "2.6.8", + "detect-node": "2.0.3", + "hpack.js": "2.1.6", + "obuf": "1.1.1", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", + "wbuf": "1.7.2" } }, "sshpk": { - "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "requires": { - "asn1": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "bcrypt-pbkdf": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "dashdash": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "ecc-jsbn": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "getpass": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" } }, "stack-trace": { @@ -566,63 +628,73 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "stream-transform": { - "version": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz", "integrity": "sha1-fY5rTgOsR4F3j4x5UXUBv7B2Kp8=" }, "string_decoder": { - "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "tweetnacl": { - "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, "util-deprecate": { - "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "vasync": { - "version": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=", "requires": { - "verror": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz" + "verror": "1.6.0" }, "dependencies": { "extsprintf": { - "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=" }, "verror": { - "version": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", "requires": { - "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz" + "extsprintf": "1.2.0" } } } }, "verror": { - "version": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" } }, "wbuf": { - "version": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", "requires": { - "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + "minimalistic-assert": "1.0.0" } }, "winston": { @@ -639,19 +711,22 @@ } }, "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xpc-connection": { - "version": "https://registry.npmjs.org/xpc-connection/-/xpc-connection-0.1.4.tgz", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xpc-connection/-/xpc-connection-0.1.4.tgz", "integrity": "sha1-3Nf6oq7Gt6bhjMXdrQQvejTHcVY=", "optional": true, "requires": { - "nan": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + "nan": "2.6.2" } }, "yallist": { - "version": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" } } diff --git a/package.json b/package.json index 3389645..f91c06d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Intervalometer for the Bolex", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "mocha tests" }, "repository": { "type": "git", diff --git a/services/bluetooth/index.js b/services/bluetooth/index.js index 8fbe53a..82d42a6 100644 --- a/services/bluetooth/index.js +++ b/services/bluetooth/index.js @@ -4,6 +4,7 @@ const ipc = require('node-ipc') const os = require('os') const bleno = require('bleno') const util = require('util') + const wifi = require('../../lib/wifi') const BLENO_DEVICE_NAME = process.env.BLENO_DEVICE_NAME || 'my_project' diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000..e09e086 --- /dev/null +++ b/tests/index.js @@ -0,0 +1,3 @@ +'use strict' + +const intval = require('../lib/intval') \ No newline at end of file From ef514f7b0ab1c96e14121288cb9242ee5fe60597 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 08:17:29 -0400 Subject: [PATCH 009/414] Install jq as a dep for json manipulation in bash --- scripts/intval3-deps.sh | 2 +- scripts/intval3-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/intval3-deps.sh b/scripts/intval3-deps.sh index 3b1c923..70475af 100644 --- a/scripts/intval3-deps.sh +++ b/scripts/intval3-deps.sh @@ -2,7 +2,7 @@ echo "Running intval3 install script" apt-get update -apt-get install git ufw nginx -y +apt-get install git ufw nginx jq -y echo "Installing node.js dependencies.." apt-get install nodejs npm -y diff --git a/scripts/intval3-install.sh b/scripts/intval3-install.sh index ae311a6..4882762 100644 --- a/scripts/intval3-install.sh +++ b/scripts/intval3-install.sh @@ -2,7 +2,7 @@ echo "Running intval3 install script" apt-get update -apt-get install git ufw nginx -y +apt-get install git ufw nginx jq -y echo "Installing node.js dependencies.." apt-get install nodejs npm -y From acabe0e80aeddd4bd97a59163df3f723296d8c80 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 08:23:18 -0400 Subject: [PATCH 010/414] Add dev script for incorporating process env variables --- dev.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 dev.sh diff --git a/dev.sh b/dev.sh new file mode 100644 index 0000000..50f3152 --- /dev/null +++ b/dev.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +echo "Starting in dev mode" + +rm run_dev.sh +jq -r ".apps[0].env | keys[]" ./process.json | while read key ; do + echo -n "$key=\"">> run_dev.sh + echo -n "$(jq ".apps[0].env.$key" ./process.json)" >> run_dev.sh + echo -n "\" ">> run_dev.sh +done +echo -n " node services/bluetooth" >> run_dev.sh + +#cat run_dev.sh +sh run_dev.sh \ No newline at end of file From e264dc8343be52ff9a1fc2b30212a925372862e4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 08:23:32 -0400 Subject: [PATCH 011/414] Ignore generated .sh --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b512c09..947371b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +run_dev.sh \ No newline at end of file From ddf89cf6aade6bab62ea8540b64f94624c93c7b8 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 08:23:52 -0400 Subject: [PATCH 012/414] Start intval logic in module --- lib/intval/index.js | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 3b291fb..2d1f7dc 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -33,7 +33,11 @@ const PINS = { class Intval { constructor () { this._pin = {} - this._startFrame = 0 + this._state = { + dir : true, //forward + running : false, + startFrame : 0 + } this._declarePins() } _declarePins () { @@ -53,12 +57,25 @@ class Intval { this._pin.fwd.set(0) this._pin.bwd.set(1) } - _watchRelease (err, val) { + _watchMicro (err, val) { if (err) { - console.error(err); + console.error(err) } } + _watchRelease (err, val) { + if (err) { + console.error(err) + } + } + setDir (val = true) { + if (typeof val !== 'boolean') { + return console.warn('Direction must be represented as either true or false') + } + this._state.dir = val + } frame (dir = true, time = 0, delay = 0) { + this._state.running = true + this._pin.micro.watch(this._watchMicro) if (delay !== 0) { setTimeout(function () { if (dir) { @@ -76,8 +93,14 @@ class Intval { } } } + _stop () { + this._pin.fwd.set(0) + this._pin.bwd.set(0) + this._pin.micro.unwatch() + this._state.running = false + } status () { - return {} + return this._state } } From cf537af849897f496d0afc24e5b056158aec6148 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 22:27:37 -0400 Subject: [PATCH 013/414] Add intval3 index to process file, for pm2 --- process.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/process.json b/process.json index ca00b64..a98129b 100644 --- a/process.json +++ b/process.json @@ -12,6 +12,12 @@ "WIFI_ID" : "3fe7d9cf-7bd2-4ff0-97c5-ebe87288c2cc", "PORT" : 6699 } + }, + { + "name" : "intval3", + "script" : "./index.js", + "watch" : false, + "env" : {} } ] } \ No newline at end of file From c87d425c57e896dc183d7c053d53ca67529da17a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 29 Aug 2017 22:27:56 -0400 Subject: [PATCH 014/414] Add better state object structure And use in preliminary logic --- lib/intval/index.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 2d1f7dc..4ea39a4 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -35,8 +35,12 @@ class Intval { this._pin = {} this._state = { dir : true, //forward - running : false, - startFrame : 0 + frame : { + start : 0, + active : false, + val : 0, + expected : 0 + } } this._declarePins() } @@ -61,6 +65,8 @@ class Intval { if (err) { console.error(err) } + this._state.frame.val = val + //determine when to stop } _watchRelease (err, val) { if (err) { @@ -74,7 +80,8 @@ class Intval { this._state.dir = val } frame (dir = true, time = 0, delay = 0) { - this._state.running = true + this._state.frame.start = +new Date() + this._state.frame.active = true this._pin.micro.watch(this._watchMicro) if (delay !== 0) { setTimeout(function () { @@ -96,8 +103,13 @@ class Intval { _stop () { this._pin.fwd.set(0) this._pin.bwd.set(0) + + let len = (+new Date()) - this._state.frame.start + + console.log(`Frame stopped`, { ms : len }) + this._pin.micro.unwatch() - this._state.running = false + this._state.frame.active = false } status () { return this._state From a0729ff9d9d81512df3dffebeedef3ae40e4bb46 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 30 Aug 2017 08:59:27 -0400 Subject: [PATCH 015/414] Write using template string, not object --- lib/intval/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 4ea39a4..8c7c6e1 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -72,6 +72,7 @@ class Intval { if (err) { console.error(err) } + console.log(`Release switch val: ${val}`) } setDir (val = true) { if (typeof val !== 'boolean') { @@ -106,7 +107,7 @@ class Intval { let len = (+new Date()) - this._state.frame.start - console.log(`Frame stopped`, { ms : len }) + console.log(`Frame stopped ${len}ms`) this._pin.micro.unwatch() this._state.frame.active = false From 32625bbbde0607f30295fdb23cbadca7f787f4c8 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 13:35:39 -0400 Subject: [PATCH 016/414] Unexport buttons on uncaught exception --- lib/intval/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 8c7c6e1..cbf2e44 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -43,6 +43,9 @@ class Intval { } } this._declarePins() + process.on('SIGINT', () => { + this._undeclarePins() + }) } _declarePins () { this._pin.fwd = Gpio(4, 'out') @@ -52,7 +55,13 @@ class Intval { this._pin.release.watch(this._watchRelease) } - _startFwd () { + _undeclarePins () { + this._pin.fwd.unexport() + this._pin.bwd.unexport() + this._pin.micro.unexport() + this._pin.release.unexport() + } + _startFwd () { this._pin.fwd.set(1) this._pin.bwd.set(0) //start high-cpu watch From 996e0ca81a04240d074b12aeea71f2a789ff6d35 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 14:47:33 -0400 Subject: [PATCH 017/414] Add gpio.js test file --- tests/gpio.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/gpio.js diff --git a/tests/gpio.js b/tests/gpio.js new file mode 100644 index 0000000..e87745c --- /dev/null +++ b/tests/gpio.js @@ -0,0 +1,13 @@ +'use strict' + +const Gpio = require('onoff').Gpio + +const btn = Gpio(18, 'in', 'both') + +console.log('Watching input on button 18') +btn.watch((err, val) => { + if (err) { + return console.error(err) + } + console.log(val) +}) \ No newline at end of file From c8f9236a9dcf63a136068578821d0d72cb3355c8 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 14:51:37 -0400 Subject: [PATCH 018/414] Pin 12 not GPIO 18? --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index e87745c..e452b42 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -2,7 +2,7 @@ const Gpio = require('onoff').Gpio -const btn = Gpio(18, 'in', 'both') +const btn = Gpio(12, 'in', 'both') console.log('Watching input on button 18') btn.watch((err, val) => { From 47abb3b8e5cb8f769ce71af9f6989114fb9ac647 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 14:54:40 -0400 Subject: [PATCH 019/414] Watch not responding to shorts, use set interval with a readSync() --- tests/gpio.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index e452b42..88d655a 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -2,12 +2,17 @@ const Gpio = require('onoff').Gpio -const btn = Gpio(12, 'in', 'both') +const btn = Gpio(18, 'in', 'both') console.log('Watching input on button 18') -btn.watch((err, val) => { + +/*btn.watch((err, val) => { if (err) { return console.error(err) } console.log(val) -}) \ No newline at end of file +})*/ + +setInterval(() => { + console.log(btn.readSync()) +}, 1000) \ No newline at end of file From 28810e761dd0541a7bcf8c51770fcf37bbc958af Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 15:07:34 -0400 Subject: [PATCH 020/414] Try gpio05 --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 88d655a..5638932 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -2,9 +2,9 @@ const Gpio = require('onoff').Gpio -const btn = Gpio(18, 'in', 'both') +const btn = Gpio(5, 'in', 'both') -console.log('Watching input on button 18') +console.log('Watching input on GPIO 05') /*btn.watch((err, val) => { if (err) { From d2e5c0f99812aa8bd32f9a98e0e6d285aaae104c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 16 Sep 2017 15:09:29 -0400 Subject: [PATCH 021/414] GPIO 05 works with pulldown resistor Now try with built in watch command --- tests/gpio.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 5638932..56d4a76 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -6,13 +6,13 @@ const btn = Gpio(5, 'in', 'both') console.log('Watching input on GPIO 05') -/*btn.watch((err, val) => { +btn.watch((err, val) => { if (err) { return console.error(err) } console.log(val) -})*/ +}) -setInterval(() => { +/*setInterval(() => { console.log(btn.readSync()) -}, 1000) \ No newline at end of file +}, 1000)*/ \ No newline at end of file From 9f308f035f58977b167004eab6c9904e1991c7d0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 15:13:40 -0400 Subject: [PATCH 022/414] Install jsdoc-to-markdown for documentation For adding documentation to libraries --- docs.sh | 8 + lib/blootstrap/Readme.md | 0 lib/intval/Readme.md | 0 lib/onoffsim/Readme.md | 0 lib/wifi/Readme.md | 0 package-lock.json | 750 ++++++++++++++++++++++++++++++++++++++- package.json | 6 +- 7 files changed, 751 insertions(+), 13 deletions(-) create mode 100644 docs.sh create mode 100644 lib/blootstrap/Readme.md create mode 100644 lib/intval/Readme.md create mode 100644 lib/onoffsim/Readme.md create mode 100644 lib/wifi/Readme.md diff --git a/docs.sh b/docs.sh new file mode 100644 index 0000000..a4c254c --- /dev/null +++ b/docs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +libs="./lib/*" +for l in $libs +do + echo "Generating documentation for $l" + ./node_modules/.bin/jsdoc2md $l/index.js > $l/Readme.md +done \ No newline at end of file diff --git a/lib/blootstrap/Readme.md b/lib/blootstrap/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/onoffsim/Readme.md b/lib/onoffsim/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/wifi/Readme.md b/lib/wifi/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 9335397..d134ef3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,57 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-escape-sequences": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-3.0.0.tgz", + "integrity": "sha1-HBg5S2r5t2/5pjUJ+kl2af0s5T4=", + "dev": true, + "requires": { + "array-back": "1.0.4" + } + }, + "app-usage-stats": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/app-usage-stats/-/app-usage-stats-0.5.1.tgz", + "integrity": "sha1-ZUfF25urCqX1ssVg6syK8g0KsTs=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "home-path": "1.0.5", + "test-value": "2.1.0", + "usage-stats": "0.9.4" + } + }, + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "2.6.1" + } + }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", @@ -22,8 +73,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -49,6 +99,12 @@ "xpc-connection": "0.1.4" } }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, "bplist-parser": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", @@ -59,7 +115,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -76,6 +131,37 @@ "safe-json-stringify": "1.0.4" } }, + "cache-point": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-0.4.1.tgz", + "integrity": "sha512-4TgWfe9SF+bUy5cCql8gWHqKNrviufNwSYxLjf2utB0pY4+bdcuFwMmY1hDB+67Gz/L1vmhFNhePAjJTFBtV+Q==", + "dev": true, + "requires": { + "array-back": "2.0.0", + "fs-then-native": "2.0.0", + "mkdirp2": "1.0.3" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, + "catharsis": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "dev": true, + "requires": { + "underscore-contrib": "0.3.0" + } + }, "clone-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.0.tgz", @@ -85,16 +171,107 @@ "is-supported-regexp-flag": "1.0.0" } }, + "collect-all": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.3.tgz", + "integrity": "sha512-0y0rBgoX8IzIjBAUnO73SEtSb4Mhk3IoceWJq5zZSxb9mWORhWH8xLYo4EDSOE1jRBk1LhmfjqWFFt10h/+MEA==", + "dev": true, + "requires": { + "stream-connect": "1.0.2", + "stream-via": "1.0.4" + } + }, "colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" }, + "command-line-args": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-4.0.7.tgz", + "integrity": "sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA==", + "dev": true, + "requires": { + "array-back": "2.0.0", + "find-replace": "1.0.3", + "typical": "2.6.1" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, + "command-line-tool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.7.0.tgz", + "integrity": "sha1-yoB5KuIGnPfKpWLAy8LNEYERIqA=", + "dev": true, + "requires": { + "ansi-escape-sequences": "3.0.0", + "array-back": "1.0.4", + "command-line-args": "4.0.7", + "command-line-usage": "4.0.1", + "typical": "2.6.1" + } + }, + "command-line-usage": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.0.1.tgz", + "integrity": "sha512-IqYzZuXizukrdhnbdUj2hh4iceycow+Jn10mER4lwU4IapYvl5ZzoRPsj5Yraew5oRk4yfFKMuULGvAfb5o29w==", + "dev": true, + "requires": { + "ansi-escape-sequences": "4.0.0", + "array-back": "2.0.0", + "table-layout": "0.4.2", + "typical": "2.6.1" + }, + "dependencies": { + "ansi-escape-sequences": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.0.0.tgz", + "integrity": "sha512-v+0wW9Wezwsyb0uF4aBVCjmSqit3Ru7PZFziGF0o2KwTvN2zWfTi3BRLq9EkJFdg3eBbyERXGTntVpBxH1J68Q==", + "dev": true, + "requires": { + "array-back": "2.0.0" + } + }, + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, + "common-sequence": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-1.0.2.tgz", + "integrity": "sha1-MOB/P49vf5s97oVPILLTnu4Ibeg=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha1-ZnZjWQUFooO/JqSE1oSJ10xUhdo=", + "dev": true, + "requires": { + "walk-back": "2.0.1" + } }, "core-util-is": { "version": "1.0.2", @@ -159,11 +336,51 @@ "ms": "2.0.0" } }, + "deep-extend": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.0.tgz", + "integrity": "sha1-bvSgmwX5iw41jW2T1Mo8rsZnKAM=", + "dev": true + }, + "defer-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defer-promise/-/defer-promise-1.0.1.tgz", + "integrity": "sha1-HKb/7dvO8XFd16riXHYW+a4iky8=", + "dev": true + }, "detect-node": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, + "dmd": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-3.0.6.tgz", + "integrity": "sha1-lMDg+4jRy2uCg3WVBT3nkZx1PCU=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "cache-point": "0.4.1", + "common-sequence": "1.0.2", + "file-set": "1.1.1", + "handlebars": "3.0.3", + "marked": "0.3.6", + "object-get": "2.1.0", + "reduce-flatten": "1.0.1", + "reduce-unique": "1.0.0", + "reduce-without": "1.0.1", + "test-value": "2.1.0", + "walk-back": "3.0.0" + }, + "dependencies": { + "walk-back": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-3.0.0.tgz", + "integrity": "sha1-I1h4ejXakQMtrV6S+AsSNw2HlcU=", + "dev": true + } + } + }, "dtrace-provider": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", @@ -201,6 +418,22 @@ "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "espree": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", + "integrity": "sha1-/V3ux2qXpRIKnNOnyxF3oJI7EdI=", + "dev": true, + "requires": { + "acorn": "3.3.0", + "acorn-jsx": "3.0.1" + } + }, "event-pubsub": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", @@ -216,11 +449,59 @@ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" }, + "file-set": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-1.1.1.tgz", + "integrity": "sha1-0+xwwIDsjxjyBLod4QZ4DJBWkms=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "find-replace": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-1.0.3.tgz", + "integrity": "sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "test-value": "2.1.0" + } + }, "formidable": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=" }, + "fs-then-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -247,11 +528,35 @@ "resolved": "https://registry.npmjs.org/gpio/-/gpio-0.2.7.tgz", "integrity": "sha1-vzhsiJYe/QpPag/3FnrA/zmX6rk=" }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true, + "optional": true + }, "handle-thing": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=" }, + "handlebars": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-3.0.3.tgz", + "integrity": "sha1-DgllGi8Ps8lJFgWDcQ1VH5Lm0q0=", + "dev": true, + "requires": { + "optimist": "0.6.1", + "source-map": "0.1.43", + "uglify-js": "2.3.6" + } + }, + "home-path": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.5.tgz", + "integrity": "sha1-eIspgVsS1Tus9XVkhHbm+QQdEz8=", + "dev": true + }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -282,7 +587,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -326,12 +630,94 @@ "easy-stack": "1.0.0" } }, + "js2xmlparser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-1.0.0.tgz", + "integrity": "sha1-WhcPLo1kds5FQF4EgjJCUTeC/jA=", + "dev": true + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, + "jsdoc-75lb": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jsdoc-75lb/-/jsdoc-75lb-3.6.0.tgz", + "integrity": "sha1-qAcRlSi0AJzLyrSbdSL2P+xs0L0=", + "dev": true, + "requires": { + "bluebird": "3.4.7", + "catharsis": "0.8.9", + "escape-string-regexp": "1.0.5", + "espree": "3.1.7", + "js2xmlparser": "1.0.0", + "klaw": "1.3.1", + "marked": "0.3.6", + "mkdirp": "0.5.1", + "requizzle": "0.2.1", + "strip-json-comments": "2.0.1", + "taffydb": "2.6.2", + "underscore": "1.8.3" + } + }, + "jsdoc-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-3.0.0.tgz", + "integrity": "sha1-DVJwAjX4Zb1Ki61evB77Vi/IrSo=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "cache-point": "0.4.1", + "collect-all": "1.0.3", + "file-set": "1.1.1", + "fs-then-native": "2.0.0", + "jsdoc-75lb": "3.6.0", + "object-to-spawn-args": "1.1.1", + "temp-path": "1.0.0", + "walk-back": "2.0.1" + } + }, + "jsdoc-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-3.0.0.tgz", + "integrity": "sha1-JxUx2I8Z3yUgsWMqf2yYlEGof94=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "lodash.omit": "4.5.0", + "lodash.pick": "4.4.0", + "reduce-extract": "1.0.0", + "sort-array": "1.1.2", + "test-value": "2.1.0" + } + }, + "jsdoc-to-markdown": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-3.0.0.tgz", + "integrity": "sha1-zIqU8fQSrB2kusFldHWwl17oFho=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "command-line-tool": "0.7.0", + "config-master": "3.1.0", + "dmd": "3.0.6", + "jsdoc-api": "3.0.0", + "jsdoc-parse": "3.0.0", + "jsdoc2md-stats": "2.0.1", + "walk-back": "2.0.1" + } + }, + "jsdoc2md-stats": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsdoc2md-stats/-/jsdoc2md-stats-2.0.1.tgz", + "integrity": "sha1-vYNDc0z+aeqAUKF5MSUSk/DZBHs=", + "dev": true, + "requires": { + "app-usage-stats": "0.5.1" + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -348,6 +734,15 @@ "verror": "1.10.0" } }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", @@ -358,6 +753,24 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "dev": true + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, "lru-cache": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", @@ -367,6 +780,12 @@ "yallist": "2.1.2" } }, + "marked": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=", + "dev": true + }, "mime": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", @@ -381,7 +800,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "1.1.8" } @@ -389,18 +807,22 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, "requires": { "minimist": "0.0.8" } }, + "mkdirp2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.3.tgz", + "integrity": "sha1-zI3YJl8fBuLY9bELblL04FC+0hs=", + "dev": true + }, "moment": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", @@ -456,6 +878,18 @@ "js-queue": "2.0.0" } }, + "object-get": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.0.tgz", + "integrity": "sha1-ciu9tgA576R8rTxtws5RqFwCxa4=", + "dev": true + }, + "object-to-spawn-args": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-1.1.1.tgz", + "integrity": "sha1-d9qIJ/Bz0BHJ4bFz+JV4FHAkZ4U=", + "dev": true + }, "obuf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", @@ -477,11 +911,20 @@ "epoll": "0.1.22" } }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { "version": "1.0.7", @@ -512,6 +955,89 @@ "util-deprecate": "1.0.2" } }, + "reduce-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", + "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "dev": true, + "requires": { + "test-value": "1.1.0" + }, + "dependencies": { + "test-value": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", + "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "typical": "2.6.1" + } + } + } + }, + "reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "dev": true + }, + "reduce-unique": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-1.0.0.tgz", + "integrity": "sha1-flhrz4ek4ytter2Cd/rWzeyfSAM=", + "dev": true + }, + "reduce-without": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "dev": true, + "requires": { + "test-value": "2.1.0" + } + }, + "req-then": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/req-then/-/req-then-0.6.4.tgz", + "integrity": "sha512-Uf7xsK1qPqPUetESHemNQ7nGtgOxngSFtlcAOOkx0lDAo+XRZpEA9QDrGBdyOfGq4b+a0z/D5gR2VJ+pp/dzBA==", + "dev": true, + "requires": { + "array-back": "2.0.0", + "defer-promise": "1.0.1", + "lodash.pick": "4.4.0", + "stream-read-all": "0.1.2", + "typical": "2.6.1" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, + "requizzle": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "dev": true, + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, "restify": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/restify/-/restify-5.2.0.tgz", @@ -580,6 +1106,26 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, + "sort-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-1.1.2.tgz", + "integrity": "sha1-uImGBTwBcKf53mPxiknsecJMPmQ=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "object-get": "2.1.0", + "typical": "2.6.1" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + }, "spdy": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", @@ -627,11 +1173,32 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, + "stream-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "dev": true, + "requires": { + "array-back": "1.0.4" + } + }, + "stream-read-all": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-0.1.2.tgz", + "integrity": "sha512-KX42xBg853m+KnwRtwCKT95ShopAbY/MNKs2dBQ0WkNeuJdqgQYRtGRbTlxdx0L6t979h3z/wMq2eMSAu7Tygw==", + "dev": true + }, "stream-transform": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz", "integrity": "sha1-fY5rTgOsR4F3j4x5UXUBv7B2Kp8=" }, + "stream-via": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", + "dev": true + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", @@ -640,12 +1207,149 @@ "safe-buffer": "5.1.1" } }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "table-layout": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.2.tgz", + "integrity": "sha512-tygyl5+eSHj4chpq5Zfy6cpc7MOUBClAW9ozghFH7hg9bAUzShOYn+/vUzTRkKOSLJWKfgYtP2tAU2c0oAD8eg==", + "dev": true, + "requires": { + "array-back": "2.0.0", + "deep-extend": "0.5.0", + "lodash.padend": "4.6.1", + "typical": "2.6.1", + "wordwrapjs": "3.0.0" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "temp-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "dev": true + }, + "test-value": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "dev": true, + "requires": { + "array-back": "1.0.4", + "typical": "2.6.1" + } + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, + "typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "dev": true + }, + "uglify-js": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz", + "integrity": "sha1-+gmEdwtCi3qbKoBY9GNV0U/vIRo=", + "dev": true, + "optional": true, + "requires": { + "async": "0.2.10", + "optimist": "0.3.7", + "source-map": "0.1.43" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true, + "optional": true + }, + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dev": true, + "optional": true, + "requires": { + "wordwrap": "0.0.3" + } + } + } + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "dev": true + }, + "underscore-contrib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "dev": true, + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, + "usage-stats": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/usage-stats/-/usage-stats-0.9.4.tgz", + "integrity": "sha512-u94pT+UDv+ZcjA5PT4KuMR67JL6VqeaQU6RN83Gf7yTqGrD/WqgCwhaj1DAKETR/KrteWXL0XZwlkDJuLJdtkg==", + "dev": true, + "requires": { + "array-back": "2.0.0", + "home-path": "1.0.5", + "mkdirp2": "1.0.3", + "req-then": "0.6.4", + "typical": "2.6.1", + "uuid": "3.1.0" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "2.6.1" + } + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -689,6 +1393,12 @@ "extsprintf": "1.3.0" } }, + "walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha1-VU4qnYdPrEeoywBr9EwvDEmYoKQ=", + "dev": true + }, "wbuf": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", @@ -710,6 +1420,22 @@ "stack-trace": "0.0.10" } }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "requires": { + "reduce-flatten": "1.0.1", + "typical": "2.6.1" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index f91c06d..d895785 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Intervalometer for the Bolex", "main": "index.js", "scripts": { - "test": "mocha tests" + "test": "mocha tests", + "docs" : "sh docs.sh" }, "repository": { "type": "git", @@ -31,5 +32,8 @@ "restify": "^5.2.0", "uuid": "^3.1.0", "winston": "^2.3.1" + }, + "devDependencies": { + "jsdoc-to-markdown": "^3.0.0" } } From e3393e6e7d24f0837d23562ca300e38218ba6a21 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 15:20:38 -0400 Subject: [PATCH 023/414] Add documentation to onoffsim --- lib/onoffsim/Readme.md | 21 +++++++++++++++++++++ lib/onoffsim/index.js | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/onoffsim/Readme.md b/lib/onoffsim/Readme.md index e69de29..1825111 100644 --- a/lib/onoffsim/Readme.md +++ b/lib/onoffsim/Readme.md @@ -0,0 +1,21 @@ + + +## onoffsim +Object representing a fake onoff Gpio class + +**Kind**: global constant + + +### onoffsim.Gpio(no, dir, additional) ⇒ object +Gpio() - +Create's a Gpio class in the case of running on a dev machine + +**Kind**: static method of [onoffsim](#onoffsim) +**Returns**: object - Fake Gpio object + +| Param | Type | Description | +| --- | --- | --- | +| no | integer | Number of the GPIO pin | +| dir | string | Dirction of the pin, 'input' or 'output' | +| additional | string | Additional instructions for the GPIO pin, for 'input' type | + diff --git a/lib/onoffsim/index.js b/lib/onoffsim/index.js index b45bb01..cd3ca77 100644 --- a/lib/onoffsim/index.js +++ b/lib/onoffsim/index.js @@ -1,6 +1,16 @@ 'use strict' +/** Object representing a fake onoff Gpio class */ const onoffsim = { + /** + * Gpio() - + * Returns a Gpio class in the case of running on a dev machine + * + * @param {integer} no Number of the GPIO pin + * @param {string} dir Dirction of the pin, 'input' or 'output' + * @param {string} additional Additional instructions for the GPIO pin, for 'input' type + * @returns {object} Fake Gpio object + */ Gpio : function (no, dir = 'in', additional = 'none') { // return { From b60334d4b790d057fe8a0ed58ad4e661c2a973ce Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 15:50:14 -0400 Subject: [PATCH 024/414] Add documentation to wifi library --- lib/onoffsim/Readme.md | 2 +- lib/onoffsim/index.js | 1 - lib/wifi/Readme.md | 109 +++++++++++++++++++++++++++++++++++++++++ lib/wifi/index.js | 56 ++++++++++++++++++++- 4 files changed, 164 insertions(+), 4 deletions(-) diff --git a/lib/onoffsim/Readme.md b/lib/onoffsim/Readme.md index 1825111..b2bc825 100644 --- a/lib/onoffsim/Readme.md +++ b/lib/onoffsim/Readme.md @@ -8,7 +8,7 @@ Object representing a fake onoff Gpio class ### onoffsim.Gpio(no, dir, additional) ⇒ object Gpio() - -Create's a Gpio class in the case of running on a dev machine +Returns a Gpio class in the case of running on a dev machine **Kind**: static method of [onoffsim](#onoffsim) **Returns**: object - Fake Gpio object diff --git a/lib/onoffsim/index.js b/lib/onoffsim/index.js index cd3ca77..dba05d1 100644 --- a/lib/onoffsim/index.js +++ b/lib/onoffsim/index.js @@ -12,7 +12,6 @@ const onoffsim = { * @returns {object} Fake Gpio object */ Gpio : function (no, dir = 'in', additional = 'none') { - // return { no : no, dir : dir, diff --git a/lib/wifi/Readme.md b/lib/wifi/Readme.md index e69de29..7d4ef84 100644 --- a/lib/wifi/Readme.md +++ b/lib/wifi/Readme.md @@ -0,0 +1,109 @@ + + +## Wifi +Class representing the wifi features + +**Kind**: global class + +* [Wifi](#Wifi) + * [.list(callback)](#Wifi+list) + * [._readConfigCb(err, data)](#Wifi+_readConfigCb) + * [._writeConfigCb(err)](#Wifi+_writeConfigCb) + * [._reconfigureCb(err, stdout, stderr)](#Wifi+_reconfigureCb) + * [._refreshCb(err, stdout, stderr)](#Wifi+_refreshCb) + * [.setNetwork(ssid, pwd, callback)](#Wifi+setNetwork) + * [.getNetwork(callback)](#Wifi+getNetwork) + + + +### wifi.list(callback) +Wifi.list() - +List available wifi access points + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| callback | function | Function which gets invoked after list is returned | + + + +### wifi._readConfigCb(err, data) +Wifi._readConfigCb() - +(internal function) Invoked after config file is read, +then invokes file write on the config file + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | (optional) Error object only present if problem reading config file | +| data | string | Contents of the config file | + + + +### wifi._writeConfigCb(err) +Wifi._writeConfigCb() - +(internal function) Invoked after config file is written, +then executes reconfiguration command + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | (optional) Error object only present if problem writing config file | + + + +### wifi._reconfigureCb(err, stdout, stderr) +Wifi._reconfigureCb() - +(internal function) Invoked after reconfiguration command is complete + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | (optional) Error object only present if configuration command fails | +| stdout | string | Standard output from reconfiguration command | +| stderr | string | Error output from command if fails | + + + +### wifi._refreshCb(err, stdout, stderr) +Wifi._refreshCb() - +(internal function) Invoked after wifi refresh command is complete + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | (optional) Error object only present if refresh command fails | +| stdout | string | Standard output from refresh command | +| stderr | string | Error output from command if fails | + + + +### wifi.setNetwork(ssid, pwd, callback) +Wifi.setNetwork() - +Function which initializes the processes for adding a wifi access point authentication + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| ssid | string | SSID of network to configure | +| pwd | string | Password of access point, plaintext | +| callback | function | Function invoked after process is complete, or fails | + + + +### wifi.getNetwork(callback) +Wifi.getNetwork() - +Executes command which gets the currently connected network + +**Kind**: instance method of [Wifi](#Wifi) + +| Param | Type | Description | +| --- | --- | --- | +| callback | function | Function which is invoked after command is completed | + diff --git a/lib/wifi/index.js b/lib/wifi/index.js index f330df4..22096b6 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -12,12 +12,19 @@ const iwgetid = '/sbin/iwgetid' const exec = require('child_process').exec const fs = require('fs') -class wifi { +/** Class representing the wifi features */ +class Wifi { constructor () { this._callback = () => {} this._entry = null this._ssid = null } + /** + * Wifi.list() - + * List available wifi access points + * + * @param {function} callback Function which gets invoked after list is returned + */ list (callback) { exec(iwlist, (err, stdout, stderr) => { if (err) { @@ -37,6 +44,14 @@ class wifi { return callback(null, output) }) } + /** + * Wifi._readConfigCb() - + * (internal function) Invoked after config file is read, + * then invokes file write on the config file + * + * @param {object} err (optional) Error object only present if problem reading config file + * @param {string} data Contents of the config file + */ _readConfigCb (err, data) { if (err) { console.error(err) @@ -50,6 +65,13 @@ class wifi { this._entry = null fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } + /** + * Wifi._writeConfigCb() - + * (internal function) Invoked after config file is written, + * then executes reconfiguration command + * + * @param {object} err (optional) Error object only present if problem writing config file + */ _writeConfigCb (err) { if (err) { console.error(err) @@ -57,6 +79,14 @@ class wifi { } exec(reconfigure, this._reconfigureCb) } + /** + * Wifi._reconfigureCb() - + * (internal function) Invoked after reconfiguration command is complete + * + * @param {object} err (optional) Error object only present if configuration command fails + * @param {string} stdout Standard output from reconfiguration command + * @param {string} stderr Error output from command if fails + */ _reconfigureCb (err, stdout, stderr) { if (err) { console.error(err) @@ -65,6 +95,14 @@ class wifi { console.log('Wifi reconfigured') exec(refresh, this._refreshCb) } + /** + * Wifi._refreshCb() - + * (internal function) Invoked after wifi refresh command is complete + * + * @param {object} err (optional) Error object only present if refresh command fails + * @param {string} stdout Standard output from refresh command + * @param {string} stderr Error output from command if fails + */ _refreshCb (err, stdout, stderr) { if (err) { console.error(err) @@ -74,12 +112,26 @@ class wifi { //this._callback(null, { ssid : ssid, pwd : pwd.length }) this._callback = () => {} } + /** + * Wifi.setNetwork() - + * Function which initializes the processes for adding a wifi access point authentication + * + * @param {string} ssid SSID of network to configure + * @param {string} pwd Password of access point, plaintext + * @param {function} callback Function invoked after process is complete, or fails + */ setNetwork (ssid, pwd, callback) { this._entry = `network={\n\tssid="${ssid}"\n\tpsk="${pwd}"\n}\n` this._callback = callback this._ssid = ssid fs.readFile(filePath, 'utf8', this._readConfigCb) } + /** + * Wifi.getNetwork() - + * Executes command which gets the currently connected network + * + * @param {function} callback Function which is invoked after command is completed + */ getNetwork (callback) { exec(iwgetid, (err, stdout, stderr) => { if (err) { @@ -90,4 +142,4 @@ class wifi { } } -module.exports = new wifi() \ No newline at end of file +module.exports = new Wifi() \ No newline at end of file From c49c6a5b8dcece4c33be5e3ed0204f2fb312370c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 17:54:07 -0400 Subject: [PATCH 025/414] Documentation for blootstrap --- lib/blootstrap/Readme.md | 6 ++++++ lib/blootstrap/index.js | 8 ++++++++ lib/intval/Readme.md | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/lib/blootstrap/Readme.md b/lib/blootstrap/Readme.md index e69de29..017bab5 100644 --- a/lib/blootstrap/Readme.md +++ b/lib/blootstrap/Readme.md @@ -0,0 +1,6 @@ + + +## Blootstrap +Class representing the bluetooth interface + +**Kind**: global class diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index 33d9da0..445086b 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -6,6 +6,7 @@ function capitalize (s) { return s[0].toUpperCase() + s.slice(1) } +/** Class representing the bluetooth interface */ class Blootstrap { constructor () { this._onData = () => {} @@ -24,6 +25,13 @@ class Blootstrap { }) }) } + /** + * Blootstrap.on() - + * Binds functions to events that are triggered by BLE messages + * + * @param {string} eventName Name of the event to to bind + * @param {function} callback Invoked when the event is triggered + */ on (eventName, callback) { this[`_on${capitalize(eventName)}`] = callback } diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index e69de29..07e7198 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -0,0 +1,6 @@ + + +## Intval +Class representing the intval3 features + +**Kind**: global class From 418d33c8f721fa53a872acd1ec8a0c0b496df2ba Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 19:51:33 -0400 Subject: [PATCH 026/414] Updated default behavior of frame Also added a parallel example of the logic to gpio.js --- lib/blootstrap/Readme.md | 13 ++++++ lib/intval/Readme.md | 35 ++++++++++++++++ lib/intval/index.js | 89 +++++++++++++++++++++++++++++++++++----- tests/gpio.js | 23 ++++++++++- 4 files changed, 149 insertions(+), 11 deletions(-) diff --git a/lib/blootstrap/Readme.md b/lib/blootstrap/Readme.md index 017bab5..e1b720a 100644 --- a/lib/blootstrap/Readme.md +++ b/lib/blootstrap/Readme.md @@ -4,3 +4,16 @@ Class representing the bluetooth interface **Kind**: global class + + +### blootstrap.on(eventName, callback) +Blootstrap.on() - +Binds functions to events that are triggered by BLE messages + +**Kind**: instance method of [Blootstrap](#Blootstrap) + +| Param | Type | Description | +| --- | --- | --- | +| eventName | string | Name of the event to to bind | +| callback | function | Invoked when the event is triggered | + diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 07e7198..176e2b7 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -4,3 +4,38 @@ Class representing the intval3 features **Kind**: global class + +* [Intval](#Intval) + * [._declarePins()](#Intval+_declarePins) + * [._undeclarePins()](#Intval+_undeclarePins) + * [.frame(dir, time, delay)](#Intval+frame) + + + +### intval._declarePins() +Intval._declarePins() - +(internal function) Declares all Gpio pins that will be used + +**Kind**: instance method of [Intval](#Intval) + + +### intval._undeclarePins() +Intval._undeclarePins() - +(internal function) Undeclares all Gpio in event of uncaught error +that interupts the node process + +**Kind**: instance method of [Intval](#Intval) + + +### intval.frame(dir, time, delay) +Intval.frame() - +Begin a single frame with set variables or defaults + +**Kind**: instance method of [Intval](#Intval) + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| dir | boolean | | (optional) Direction of the frame | +| time | integer | | (optional) Exposure time, 0 = minimum | +| delay | delay | | (optional) Delay after frame before another can be started | + diff --git a/lib/intval/index.js b/lib/intval/index.js index cbf2e44..5be37e6 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -30,6 +30,7 @@ const PINS = { } } +/** Class representing the intval3 features */ class Intval { constructor () { this._pin = {} @@ -38,23 +39,38 @@ class Intval { frame : { start : 0, active : false, + time : 0, + delay : 0, val : 0, expected : 0 + }, + release : { + time: 0, + active : false } } this._declarePins() - process.on('SIGINT', () => { - this._undeclarePins() - }) + process.on('SIGINT', this._undeclarePins) } + /** + * Intval._declarePins() - + * (internal function) Declares all Gpio pins that will be used + * + */ _declarePins () { - this._pin.fwd = Gpio(4, 'out') - this._pin.bwd = Gpio(5, 'out') - this._pin.micro = Gpio(6, 'in', 'rising') - this._pin.release = Gpio(7, 'in', 'both') + this._pin.fwd = Gpio(13, 'out') + this._pin.bwd = Gpio(19, 'out') + this._pin.micro = Gpio(5, 'in', 'rising') // + this._pin.release = Gpio(6, 'in', 'both') this._pin.release.watch(this._watchRelease) } + /** + * Intval._undeclarePins() - + * (internal function) Undeclares all Gpio in event of uncaught error + * that interupts the node process + * + */ _undeclarePins () { this._pin.fwd.unexport() this._pin.bwd.unexport() @@ -71,6 +87,9 @@ class Intval { this._pin.bwd.set(1) } _watchMicro (err, val) { + /* Microswitch + 10K ohm resistor */ + /* 1 = open */ + /* 0 = closed */ if (err) { console.error(err) } @@ -78,10 +97,29 @@ class Intval { //determine when to stop } _watchRelease (err, val) { + const NOW = +new Date() + /* Button + 10K ohm resistor */ + /* 1 = open */ + /* 0 = closed */ if (err) { - console.error(err) + return console.error(err) } console.log(`Release switch val: ${val}`) + if (val === 0) { + if (this._state.release.time === 0) { + this._state.release.time = NOW + this._state.release.active = true //maybe unncecessary + } else if (this._state.release.active) { + if (NOW - this._state.release.time > 100) { + this.frame() + } else if (NOW - this._state.release.time > 1000) { + this.sequence() + } + console.log(`Release closed for ${NOW - this._state.release.time}`) + this._state.release.time = 0 + this._state.release.active = false + } + } } setDir (val = true) { if (typeof val !== 'boolean') { @@ -89,10 +127,38 @@ class Intval { } this._state.dir = val } - frame (dir = true, time = 0, delay = 0) { + /** + * Intval.frame() - + * Begin a single frame with set variables or defaults + * + * @param {boolean} dir (optional) Direction of the frame + * @param {integer} time (optional) Exposure time, 0 = minimum + * @param {delay} delay (optional) Delay after frame before another can be started + * + */ + frame (dir = null, time = null, delay = null) { //may be overloaded, delay is suspect + if (dir === true || (dir === null && this._state.dir === true) ) { + dir = true + } else { + dir = false + } + + if (time === null && this._state.time !== 0) { + time = this._state.time + } else { + time = 0 + } + + if (delay === null && this._state.delay !== 0) { + delay = this._state.delay + } else { + delay = 0 + } + this._state.frame.start = +new Date() this._state.frame.active = true this._pin.micro.watch(this._watchMicro) + if (delay !== 0) { setTimeout(function () { if (dir) { @@ -110,7 +176,10 @@ class Intval { } } } - _stop () { + sequence () { + console.log(`Started sequence`) + } + _stop () { this._pin.fwd.set(0) this._pin.bwd.set(0) diff --git a/tests/gpio.js b/tests/gpio.js index 56d4a76..ccdd1c5 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -6,11 +6,32 @@ const btn = Gpio(5, 'in', 'both') console.log('Watching input on GPIO 05') +let saveTime = 0 +let active = false btn.watch((err, val) => { + const NOW = +new Date() + /* Button + 10K ohm resistor */ + /* 1 = open */ + /* 0 = closed */ if (err) { return console.error(err) } - console.log(val) + console.log(`Release switch val: ${val}`) + if (val === 0) { + if (saveTime === 0) { + saveTime = NOW + active = true //maybe unncecessary + } else if (active) { + if (NOW - saveTime > 100) { + console.log('Started Frame') + } else if (NOW - saveTime > 1000) { + console.log('Started Sequence') + } + console.log(`Release closed for ${NOW - saveTime}`) + saveTime = 0 + active = false + } + } }) /*setInterval(() => { From 4cb28c024d65b3969c5d491a3f86f00d3a0adcb3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 19:55:35 -0400 Subject: [PATCH 027/414] Caught major oversight. 1 = open Therefore frame event needs to be determined on open, after started when closed --- tests/gpio.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index ccdd1c5..9b81e9f 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -17,11 +17,18 @@ btn.watch((err, val) => { return console.error(err) } console.log(`Release switch val: ${val}`) + if (val === 1) { + console.log('open') + } else if (val === 0) { + console.log('closed') + } if (val === 0) { if (saveTime === 0) { saveTime = NOW active = true //maybe unncecessary - } else if (active) { + } + } else if (val === 1) { + if (active) { if (NOW - saveTime > 100) { console.log('Started Frame') } else if (NOW - saveTime > 1000) { From f13d609fce053dd6fa4082b89f10b7196e335c30 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 19:57:38 -0400 Subject: [PATCH 028/414] Fixed over 1000ms sequence logic --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 9b81e9f..2d4b00c 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -29,9 +29,9 @@ btn.watch((err, val) => { } } else if (val === 1) { if (active) { - if (NOW - saveTime > 100) { + if (NOW - saveTime > 50 && NOW - saveTime < 1000) { console.log('Started Frame') - } else if (NOW - saveTime > 1000) { + } else if (NOW - saveTime >= 1000) { console.log('Started Sequence') } console.log(`Release closed for ${NOW - saveTime}`) From 2d0aaaca8c4d1471d76623b374c440f244334549 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 20:00:26 -0400 Subject: [PATCH 029/414] Remove extra log events Test running test for extended length of time to test whether or not errant 0 change events trigger frames --- tests/gpio.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 2d4b00c..a42c06d 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -16,16 +16,19 @@ btn.watch((err, val) => { if (err) { return console.error(err) } - console.log(`Release switch val: ${val}`) + //console.log(`Release switch val: ${val}`) if (val === 1) { - console.log('open') + //console.log('open') } else if (val === 0) { - console.log('closed') + //console.log('closed') } if (val === 0) { if (saveTime === 0) { saveTime = NOW active = true //maybe unncecessary + } else { + //saveTime = 0 + //active = false } } else if (val === 1) { if (active) { From 78135c4e778bf32e7de922667d24d73e4960dcb6 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 20:25:21 -0400 Subject: [PATCH 030/414] More complex logic to prevent errant closed events from breaking --- lib/intval/index.js | 17 ++++++++++++----- tests/gpio.js | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 5be37e6..5450bc8 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -49,6 +49,8 @@ class Intval { active : false } } + this._releaseMin = 50 + this._releaseSequence = 1000 this._declarePins() process.on('SIGINT', this._undeclarePins) } @@ -98,6 +100,7 @@ class Intval { } _watchRelease (err, val) { const NOW = +new Date() + let press = 0 /* Button + 10K ohm resistor */ /* 1 = open */ /* 0 = closed */ @@ -106,16 +109,20 @@ class Intval { } console.log(`Release switch val: ${val}`) if (val === 0) { - if (this._state.release.time === 0) { + if ((!this._state.release.active && this._state.release.time === 0) || (this._state.release.active && (NOW - this._state.release.time) > (this._releaseSequence * 10)) + ) { this._state.release.time = NOW this._state.release.active = true //maybe unncecessary - } else if (this._state.release.active) { - if (NOW - this._state.release.time > 100) { + } + } else if (val === 1) { + if (this._state.release.active) { + press = NOW - this._state.release.time + if (press > this._releaseMin && press < this._releaseSequence) { this.frame() - } else if (NOW - this._state.release.time > 1000) { + } else if (press >= this._releaseSequence) { this.sequence() } - console.log(`Release closed for ${NOW - this._state.release.time}`) + console.log(`Release closed for ${press}`) this._state.release.time = 0 this._state.release.active = false } diff --git a/tests/gpio.js b/tests/gpio.js index a42c06d..26ef56c 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -23,7 +23,7 @@ btn.watch((err, val) => { //console.log('closed') } if (val === 0) { - if (saveTime === 0) { + if ((!active && saveTime === 0) || (active && NOW - saveTime > 10 * 1000)) { saveTime = NOW active = true //maybe unncecessary } else { From 0fc4200f6da85355dae91bb49c54d42bc3487292 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 17 Sep 2017 20:27:51 -0400 Subject: [PATCH 031/414] Re-add watch callback console.log --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 26ef56c..ac46e7c 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -16,7 +16,7 @@ btn.watch((err, val) => { if (err) { return console.error(err) } - //console.log(`Release switch val: ${val}`) + console.log(`Release switch val: ${val}`) if (val === 1) { //console.log('open') } else if (val === 0) { From 332399cfd7784ed61eb420bb125b4e389fee6068 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 18 Sep 2017 20:46:18 -0400 Subject: [PATCH 032/414] Document intval lib --- lib/intval/Readme.md | 24 + lib/intval/index.js | 37 +- package-lock.json | 1131 +++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 4 files changed, 1171 insertions(+), 23 deletions(-) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 176e2b7..ebb5f4c 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -8,6 +8,9 @@ Class representing the intval3 features * [Intval](#Intval) * [._declarePins()](#Intval+_declarePins) * [._undeclarePins()](#Intval+_undeclarePins) + * [._startFwd()](#Intval+_startFwd) + * [._startBwd()](#Intval+_startBwd) + * [._stop()](#Intval+_stop) * [.frame(dir, time, delay)](#Intval+frame) @@ -24,6 +27,27 @@ Intval._undeclarePins() - (internal function) Undeclares all Gpio in event of uncaught error that interupts the node process +**Kind**: instance method of [Intval](#Intval) + + +### intval._startFwd() +Intval._startFwd() - +Start motor in forward direction by setting correct pins in h-bridge + +**Kind**: instance method of [Intval](#Intval) + + +### intval._startBwd() +Intval._startBwd() - +Start motor in backward direction by setting correct pins in h-bridge + +**Kind**: instance method of [Intval](#Intval) + + +### intval._stop() +Intval._stop() - +Stop motor by setting both motor pins to 0 (LOW) + **Kind**: instance method of [Intval](#Intval) diff --git a/lib/intval/index.js b/lib/intval/index.js index 5450bc8..3edb38e 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -79,15 +79,41 @@ class Intval { this._pin.micro.unexport() this._pin.release.unexport() } + /** + * Intval._startFwd() - + * Start motor in forward direction by setting correct pins in h-bridge + * + */ _startFwd () { this._pin.fwd.set(1) this._pin.bwd.set(0) //start high-cpu watch } + /** + * Intval._startBwd() - + * Start motor in backward direction by setting correct pins in h-bridge + * + */ _startBwd () { this._pin.fwd.set(0) this._pin.bwd.set(1) } + /** + * Intval._stop() - + * Stop motor by setting both motor pins to 0 (LOW) + * + */ + _stop () { + this._pin.fwd.set(0) + this._pin.bwd.set(0) + + let len = (+new Date()) - this._state.frame.start + + console.log(`Frame stopped ${len}ms`) + + this._pin.micro.unwatch() + this._state.frame.active = false + } _watchMicro (err, val) { /* Microswitch + 10K ohm resistor */ /* 1 = open */ @@ -186,17 +212,6 @@ class Intval { sequence () { console.log(`Started sequence`) } - _stop () { - this._pin.fwd.set(0) - this._pin.bwd.set(0) - - let len = (+new Date()) - this._state.frame.start - - console.log(`Frame stopped ${len}ms`) - - this._pin.micro.unwatch() - this._state.frame.active = false - } status () { return this._state } diff --git a/package-lock.json b/package-lock.json index d134ef3..bf33259 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,9 +94,9 @@ "resolved": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", "integrity": "sha1-IesK10O850eU45L0ph4TsHOT26o=", "requires": { + "bluetooth-hci-socket": "0.5.1", "bplist-parser": "0.0.6", - "debug": "2.6.8", - "xpc-connection": "0.1.4" + "debug": "2.6.8" } }, "bluebird": { @@ -105,6 +105,17 @@ "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", "dev": true }, + "bluetooth-hci-socket": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.1.tgz", + "integrity": "sha1-774hUk/Bz10/rl1RNl1WHUq77Qs=", + "optional": true, + "requires": { + "debug": "2.6.8", + "nan": "2.6.2", + "usb": "1.2.0" + } + }, "bplist-parser": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", @@ -1350,6 +1361,1113 @@ } } }, + "usb": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.2.0.tgz", + "integrity": "sha1-q3LZeGoEcGiRmgJNCMehxbHmD0c=", + "optional": true, + "requires": { + "nan": "2.6.2", + "node-pre-gyp": "0.6.30" + }, + "dependencies": { + "node-pre-gyp": { + "version": "0.6.30", + "bundled": true, + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.0.0", + "rc": "1.1.6", + "request": "2.74.0", + "rimraf": "2.5.4", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.1.4" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "bundled": true + } + } + }, + "nopt": { + "version": "3.0.6", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1.0.9" + }, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "bundled": true, + "optional": true + } + } + }, + "npmlog": { + "version": "4.0.0", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.2", + "console-control-strings": "1.1.0", + "gauge": "2.6.0", + "set-blocking": "2.0.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "1.1.2", + "bundled": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.1.5" + }, + "dependencies": { + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "readable-stream": { + "version": "2.1.5", + "bundled": true, + "optional": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.1", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "inherits": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "optional": true + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + } + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "gauge": { + "version": "2.6.0", + "bundled": true, + "optional": true, + "requires": { + "aproba": "1.0.4", + "console-control-strings": "1.1.0", + "has-color": "0.1.7", + "has-unicode": "2.0.1", + "object-assign": "4.1.0", + "signal-exit": "3.0.0", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.0.4", + "bundled": true, + "optional": true + }, + "has-color": { + "version": "0.1.7", + "bundled": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.0", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.0.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "code-point-at": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.0" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "bundled": true + } + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.0" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "bundled": true + } + } + }, + "wide-align": { + "version": "1.1.0", + "bundled": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + } + } + }, + "rc": { + "version": "1.1.6", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "0.4.1", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "1.0.4" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.1", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "strip-json-comments": { + "version": "1.0.4", + "bundled": true, + "optional": true + } + } + }, + "request": { + "version": "2.74.0", + "bundled": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.4.1", + "bl": "1.1.2", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "1.0.1", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.11", + "node-uuid": "1.4.7", + "oauth-sign": "0.8.2", + "qs": "6.2.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.1", + "tunnel-agent": "0.4.3" + }, + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "bl": { + "version": "1.1.2", + "bundled": true, + "optional": true, + "requires": { + "readable-stream": "2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.1", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "inherits": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "optional": true + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + } + } + } + } + }, + "caseless": { + "version": "0.11.0", + "bundled": true, + "optional": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + }, + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "bundled": true + } + } + }, + "extend": { + "version": "3.0.0", + "bundled": true, + "optional": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "async": "2.0.1", + "combined-stream": "1.0.5", + "mime-types": "2.1.11" + }, + "dependencies": { + "async": { + "version": "2.0.1", + "bundled": true, + "optional": true, + "requires": { + "lodash": "4.15.0" + }, + "dependencies": { + "lodash": { + "version": "4.15.0", + "bundled": true, + "optional": true + } + } + } + } + }, + "har-validator": { + "version": "2.0.6", + "bundled": true, + "optional": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.9.0", + "is-my-json-valid": "2.13.1", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "optional": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "bundled": true, + "optional": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "bundled": true, + "optional": true + } + } + }, + "supports-color": { + "version": "2.0.0", + "bundled": true, + "optional": true + } + } + }, + "commander": { + "version": "2.9.0", + "bundled": true, + "optional": true, + "requires": { + "graceful-readlink": "1.0.1" + }, + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "bundled": true, + "optional": true + } + } + }, + "is-my-json-valid": { + "version": "2.13.1", + "bundled": true, + "optional": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "2.0.0", + "xtend": "4.0.1" + }, + "dependencies": { + "generate-function": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "generate-object-property": { + "version": "1.2.0", + "bundled": true, + "optional": true, + "requires": { + "is-property": "1.0.2" + }, + "dependencies": { + "is-property": { + "version": "1.0.2", + "bundled": true, + "optional": true + } + } + }, + "jsonpointer": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true, + "optional": true + } + } + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "optional": true, + "requires": { + "pinkie": "2.0.4" + }, + "dependencies": { + "pinkie": { + "version": "2.0.4", + "bundled": true, + "optional": true + } + } + } + } + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + }, + "dependencies": { + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "optional": true, + "requires": { + "boom": "2.10.1" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "optional": true, + "requires": { + "hoek": "2.16.3" + } + } + } + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.3.0", + "sshpk": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.3.0", + "bundled": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2", + "json-schema": "0.2.2", + "verror": "1.3.6" + }, + "dependencies": { + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.2", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + } + } + }, + "sshpk": { + "version": "1.10.0", + "bundled": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.0", + "dashdash": "1.14.0", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.6", + "jodid25519": "1.0.2", + "jsbn": "0.1.0", + "tweetnacl": "0.13.3" + }, + "dependencies": { + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.3", + "bundled": true, + "optional": true + } + } + }, + "dashdash": { + "version": "1.14.0", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.0" + } + }, + "getpass": { + "version": "0.1.6", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.0" + } + }, + "jsbn": { + "version": "0.1.0", + "bundled": true, + "optional": true + }, + "tweetnacl": { + "version": "0.13.3", + "bundled": true, + "optional": true + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "mime-types": { + "version": "2.1.11", + "bundled": true, + "requires": { + "mime-db": "1.23.0" + }, + "dependencies": { + "mime-db": { + "version": "1.23.0", + "bundled": true + } + } + }, + "node-uuid": { + "version": "1.4.7", + "bundled": true, + "optional": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.2.1", + "bundled": true, + "optional": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "tough-cookie": { + "version": "2.3.1", + "bundled": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "bundled": true, + "optional": true + } + } + }, + "rimraf": { + "version": "2.5.4", + "bundled": true, + "requires": { + "glob": "7.0.6" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.5", + "inherits": "2.0.1", + "minimatch": "3.0.3", + "once": "1.3.3", + "path-is-absolute": "1.0.0" + }, + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "inflight": { + "version": "1.0.5", + "bundled": true, + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "inherits": { + "version": "2.0.1", + "bundled": true + }, + "minimatch": { + "version": "3.0.3", + "bundled": true, + "requires": { + "brace-expansion": "1.1.6" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "bundled": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + } + } + } + } + }, + "once": { + "version": "1.3.3", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.10", + "inherits": "2.0.1" + }, + "dependencies": { + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.1" + } + }, + "fstream": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.6", + "inherits": "2.0.1", + "mkdirp": "0.5.1", + "rimraf": "2.5.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.6", + "bundled": true + } + } + }, + "inherits": { + "version": "2.0.1", + "bundled": true + } + } + }, + "tar-pack": { + "version": "3.1.4", + "bundled": true, + "optional": true, + "requires": { + "debug": "2.2.0", + "fstream": "1.0.10", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.1.5", + "rimraf": "2.5.4", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "bundled": true, + "optional": true, + "requires": { + "ms": "0.7.1" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "bundled": true, + "optional": true + } + } + }, + "fstream": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.6", + "inherits": "2.0.1", + "mkdirp": "0.5.1", + "rimraf": "2.5.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.6", + "bundled": true + }, + "inherits": { + "version": "2.0.1", + "bundled": true + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true, + "requires": { + "fstream": "1.0.10", + "inherits": "2.0.1", + "minimatch": "3.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.3", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "1.1.6" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + } + } + } + } + } + } + }, + "once": { + "version": "1.3.3", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1.0.2" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.1.5", + "bundled": true, + "optional": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.1", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "inherits": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "optional": true + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + } + } + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + } + } + } + } + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1441,15 +2559,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "xpc-connection": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/xpc-connection/-/xpc-connection-0.1.4.tgz", - "integrity": "sha1-3Nf6oq7Gt6bhjMXdrQQvejTHcVY=", - "optional": true, - "requires": { - "nan": "2.6.2" - } - }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/package.json b/package.json index d895785..d1b2e33 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "mocha tests", - "docs" : "sh docs.sh" + "docs": "sh docs.sh" }, "repository": { "type": "git", From 32affb1b72b7b7adc667f9311bb90bd43c8f08a4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 Sep 2017 15:06:34 -0400 Subject: [PATCH 033/414] Use PINS configuration object to initialize pins TODO: Separate into file, automatically switching out for different models --- lib/intval/index.js | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 3edb38e..35481b6 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -11,20 +11,20 @@ try { const PINS = { fwd : { - pin : 4, + pin : 13, dir : 'out' }, bwd : { - pin : 5, + pin : 19, dir : 'out' }, micro : { pin : 6, dir : 'in', - edge : 'rising' + edge : 'both' }, release : { - pin : 7, + pin : 5, dir : 'in', edge : 'both' } @@ -60,11 +60,13 @@ class Intval { * */ _declarePins () { - this._pin.fwd = Gpio(13, 'out') - this._pin.bwd = Gpio(19, 'out') - this._pin.micro = Gpio(5, 'in', 'rising') // - this._pin.release = Gpio(6, 'in', 'both') - + let pin + for (let p in PINS) { + pin = PINS[p] + if (pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir, pin.edge) + if (!pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir) + } + console.dir(this._pin) this._pin.release.watch(this._watchRelease) } /** @@ -114,6 +116,15 @@ class Intval { this._pin.micro.unwatch() this._state.frame.active = false } + /** + * Intval._watchMicro() - + * Callback for watching microswitch state changes + * Using GPIO 06 on raspberry pi zero w + * + * @param {object} err Error object present if problem reading pin + * @param {integer} val Current value of the pin + * + */ _watchMicro (err, val) { /* Microswitch + 10K ohm resistor */ /* 1 = open */ @@ -124,6 +135,19 @@ class Intval { this._state.frame.val = val //determine when to stop } + /** + * Intval._watchRelease() - + * Callback for watching relese switch state changes + * Using GPIO 05 on raspberry pi zero w + * * If closed, start timer. + * * If opened, check timer AND + * * If time closed longer than minimum and less than `this._releaseSequence`, start frame + * * If time closed longer than `this._releaseSequence`, start sequence + * + * @param {object} err Error object present if problem reading pin + * @param {integer} val Current value of the pin + * + */ _watchRelease (err, val) { const NOW = +new Date() let press = 0 From bfb28a04488e7654b320b0e20236dd94bfa1a7f0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 Sep 2017 15:06:52 -0400 Subject: [PATCH 034/414] Add documentation to interval --- lib/intval/Readme.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index ebb5f4c..549eda3 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -11,6 +11,8 @@ Class representing the intval3 features * [._startFwd()](#Intval+_startFwd) * [._startBwd()](#Intval+_startBwd) * [._stop()](#Intval+_stop) + * [._watchMicro(err, val)](#Intval+_watchMicro) + * [._watchRelease(err, val)](#Intval+_watchRelease) * [.frame(dir, time, delay)](#Intval+frame) @@ -49,6 +51,38 @@ Intval._stop() - Stop motor by setting both motor pins to 0 (LOW) **Kind**: instance method of [Intval](#Intval) + + +### intval._watchMicro(err, val) +Intval._watchMicro() - +Callback for watching microswitch state changes +Using GPIO 06 on raspberry pi zero w + +**Kind**: instance method of [Intval](#Intval) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | Error object present if problem reading pin | +| val | integer | Current value of the pin | + + + +### intval._watchRelease(err, val) +Intval._watchRelease() - +Callback for watching relese switch state changes +Using GPIO 05 on raspberry pi zero w +* If closed, start timer. +* If opened, check timer AND +* If time closed longer than minimum and less than `this._releaseSequence`, start frame +* If time closed longer than `this._releaseSequence`, start sequence + +**Kind**: instance method of [Intval](#Intval) + +| Param | Type | Description | +| --- | --- | --- | +| err | object | Error object present if problem reading pin | +| val | integer | Current value of the pin | + ### intval.frame(dir, time, delay) From 52c0c6f35298583d6b49d1eda410908c8c331b48 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 Sep 2017 15:20:58 -0400 Subject: [PATCH 035/414] Add default optional values to documentation --- lib/intval/Readme.md | 37 ++++++++++++++++++++++++------------- lib/intval/index.js | 40 +++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 549eda3..e893021 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -13,7 +13,7 @@ Class representing the intval3 features * [._stop()](#Intval+_stop) * [._watchMicro(err, val)](#Intval+_watchMicro) * [._watchRelease(err, val)](#Intval+_watchRelease) - * [.frame(dir, time, delay)](#Intval+frame) + * [.frame([dir], [time], [delay])](#Intval+frame) @@ -55,8 +55,14 @@ Stop motor by setting both motor pins to 0 (LOW) ### intval._watchMicro(err, val) Intval._watchMicro() - -Callback for watching microswitch state changes -Using GPIO 06 on raspberry pi zero w +Callback for watching relese switch state changes. +Using GPIO 06 on Raspberry Pi Zero W. +* If closed, start timer. +* If opened, check timer AND + +Microswitch + 10K ohm resistor +* 1 === open +* 0 === closed **Kind**: instance method of [Intval](#Intval) @@ -69,12 +75,17 @@ Using GPIO 06 on raspberry pi zero w ### intval._watchRelease(err, val) Intval._watchRelease() - -Callback for watching relese switch state changes -Using GPIO 05 on raspberry pi zero w -* If closed, start timer. -* If opened, check timer AND -* If time closed longer than minimum and less than `this._releaseSequence`, start frame -* If time closed longer than `this._releaseSequence`, start sequence +Callback for watching relese switch state changes. +Using GPIO 05 on Raspberry Pi Zero W. + +1) If closed, start timer. +2) If opened, check timer AND +3) If `press` (`NOW - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame +4) If `press` greater than `this._releaseSequence`, start sequence + +Button + 10K ohm resistor +* 1 === open +* 0 === closed **Kind**: instance method of [Intval](#Intval) @@ -85,7 +96,7 @@ Using GPIO 05 on raspberry pi zero w -### intval.frame(dir, time, delay) +### intval.frame([dir], [time], [delay]) Intval.frame() - Begin a single frame with set variables or defaults @@ -93,7 +104,7 @@ Begin a single frame with set variables or defaults | Param | Type | Default | Description | | --- | --- | --- | --- | -| dir | boolean | | (optional) Direction of the frame | -| time | integer | | (optional) Exposure time, 0 = minimum | -| delay | delay | | (optional) Delay after frame before another can be started | +| [dir] | boolean | "null" | (optional) Direction of the frame | +| [time] | integer | "null" | (optional) Exposure time, 0 = minimum | +| [delay] | integer | "null" | (optional) Delay after frame before another can be started | diff --git a/lib/intval/index.js b/lib/intval/index.js index 35481b6..c3f31e0 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -118,17 +118,21 @@ class Intval { } /** * Intval._watchMicro() - - * Callback for watching microswitch state changes - * Using GPIO 06 on raspberry pi zero w + * Callback for watching relese switch state changes. + * Using GPIO 06 on Raspberry Pi Zero W. + * * If closed, start timer. + * * If opened, check timer AND + * + * Microswitch + 10K ohm resistor + * * 1 === open + * * 0 === closed + * * * @param {object} err Error object present if problem reading pin * @param {integer} val Current value of the pin * */ _watchMicro (err, val) { - /* Microswitch + 10K ohm resistor */ - /* 1 = open */ - /* 0 = closed */ if (err) { console.error(err) } @@ -137,12 +141,17 @@ class Intval { } /** * Intval._watchRelease() - - * Callback for watching relese switch state changes - * Using GPIO 05 on raspberry pi zero w - * * If closed, start timer. - * * If opened, check timer AND - * * If time closed longer than minimum and less than `this._releaseSequence`, start frame - * * If time closed longer than `this._releaseSequence`, start sequence + * Callback for watching relese switch state changes. + * Using GPIO 05 on Raspberry Pi Zero W. + * + * 1) If closed, start timer. + * 2) If opened, check timer AND + * 3) If `press` (`NOW - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame + * 4) If `press` greater than `this._releaseSequence`, start sequence + * + * Button + 10K ohm resistor + * * 1 === open + * * 0 === closed * * @param {object} err Error object present if problem reading pin * @param {integer} val Current value of the pin @@ -151,9 +160,6 @@ class Intval { _watchRelease (err, val) { const NOW = +new Date() let press = 0 - /* Button + 10K ohm resistor */ - /* 1 = open */ - /* 0 = closed */ if (err) { return console.error(err) } @@ -188,9 +194,9 @@ class Intval { * Intval.frame() - * Begin a single frame with set variables or defaults * - * @param {boolean} dir (optional) Direction of the frame - * @param {integer} time (optional) Exposure time, 0 = minimum - * @param {delay} delay (optional) Delay after frame before another can be started + * @param {?boolean} [dir="null"] (optional) Direction of the frame + * @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum + * @param {?integer} [delay="null"] (optional) Delay after frame before another can be started * */ frame (dir = null, time = null, delay = null) { //may be overloaded, delay is suspect From aadf6aef3758c618b0fc63f1abb87bc136dd1ac5 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 Sep 2017 15:23:48 -0400 Subject: [PATCH 036/414] Remove extra function name from docs jsdoc does this well enough without repeating. Does not actually improve readability in code --- lib/blootstrap/Readme.md | 1 - lib/blootstrap/index.js | 1 - lib/intval/Readme.md | 8 -------- lib/intval/index.js | 12 ++---------- lib/onoffsim/Readme.md | 1 - lib/onoffsim/index.js | 1 - lib/wifi/Readme.md | 7 ------- lib/wifi/index.js | 7 ------- 8 files changed, 2 insertions(+), 36 deletions(-) diff --git a/lib/blootstrap/Readme.md b/lib/blootstrap/Readme.md index e1b720a..a96ceb5 100644 --- a/lib/blootstrap/Readme.md +++ b/lib/blootstrap/Readme.md @@ -7,7 +7,6 @@ Class representing the bluetooth interface ### blootstrap.on(eventName, callback) -Blootstrap.on() - Binds functions to events that are triggered by BLE messages **Kind**: instance method of [Blootstrap](#Blootstrap) diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index 445086b..33e7fb0 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -26,7 +26,6 @@ class Blootstrap { }) } /** - * Blootstrap.on() - * Binds functions to events that are triggered by BLE messages * * @param {string} eventName Name of the event to to bind diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index e893021..3d4ed58 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -18,14 +18,12 @@ Class representing the intval3 features ### intval._declarePins() -Intval._declarePins() - (internal function) Declares all Gpio pins that will be used **Kind**: instance method of [Intval](#Intval) ### intval._undeclarePins() -Intval._undeclarePins() - (internal function) Undeclares all Gpio in event of uncaught error that interupts the node process @@ -33,28 +31,24 @@ that interupts the node process ### intval._startFwd() -Intval._startFwd() - Start motor in forward direction by setting correct pins in h-bridge **Kind**: instance method of [Intval](#Intval) ### intval._startBwd() -Intval._startBwd() - Start motor in backward direction by setting correct pins in h-bridge **Kind**: instance method of [Intval](#Intval) ### intval._stop() -Intval._stop() - Stop motor by setting both motor pins to 0 (LOW) **Kind**: instance method of [Intval](#Intval) ### intval._watchMicro(err, val) -Intval._watchMicro() - Callback for watching relese switch state changes. Using GPIO 06 on Raspberry Pi Zero W. * If closed, start timer. @@ -74,7 +68,6 @@ Microswitch + 10K ohm resistor ### intval._watchRelease(err, val) -Intval._watchRelease() - Callback for watching relese switch state changes. Using GPIO 05 on Raspberry Pi Zero W. @@ -97,7 +90,6 @@ Button + 10K ohm resistor ### intval.frame([dir], [time], [delay]) -Intval.frame() - Begin a single frame with set variables or defaults **Kind**: instance method of [Intval](#Intval) diff --git a/lib/intval/index.js b/lib/intval/index.js index c3f31e0..b0b7c4f 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -55,7 +55,6 @@ class Intval { process.on('SIGINT', this._undeclarePins) } /** - * Intval._declarePins() - * (internal function) Declares all Gpio pins that will be used * */ @@ -69,8 +68,7 @@ class Intval { console.dir(this._pin) this._pin.release.watch(this._watchRelease) } - /** - * Intval._undeclarePins() - + /** * (internal function) Undeclares all Gpio in event of uncaught error * that interupts the node process * @@ -82,7 +80,6 @@ class Intval { this._pin.release.unexport() } /** - * Intval._startFwd() - * Start motor in forward direction by setting correct pins in h-bridge * */ @@ -92,7 +89,6 @@ class Intval { //start high-cpu watch } /** - * Intval._startBwd() - * Start motor in backward direction by setting correct pins in h-bridge * */ @@ -100,8 +96,7 @@ class Intval { this._pin.fwd.set(0) this._pin.bwd.set(1) } - /** - * Intval._stop() - + /** * Stop motor by setting both motor pins to 0 (LOW) * */ @@ -117,7 +112,6 @@ class Intval { this._state.frame.active = false } /** - * Intval._watchMicro() - * Callback for watching relese switch state changes. * Using GPIO 06 on Raspberry Pi Zero W. * * If closed, start timer. @@ -140,7 +134,6 @@ class Intval { //determine when to stop } /** - * Intval._watchRelease() - * Callback for watching relese switch state changes. * Using GPIO 05 on Raspberry Pi Zero W. * @@ -191,7 +184,6 @@ class Intval { this._state.dir = val } /** - * Intval.frame() - * Begin a single frame with set variables or defaults * * @param {?boolean} [dir="null"] (optional) Direction of the frame diff --git a/lib/onoffsim/Readme.md b/lib/onoffsim/Readme.md index b2bc825..144b474 100644 --- a/lib/onoffsim/Readme.md +++ b/lib/onoffsim/Readme.md @@ -7,7 +7,6 @@ Object representing a fake onoff Gpio class ### onoffsim.Gpio(no, dir, additional) ⇒ object -Gpio() - Returns a Gpio class in the case of running on a dev machine **Kind**: static method of [onoffsim](#onoffsim) diff --git a/lib/onoffsim/index.js b/lib/onoffsim/index.js index dba05d1..7fac81f 100644 --- a/lib/onoffsim/index.js +++ b/lib/onoffsim/index.js @@ -3,7 +3,6 @@ /** Object representing a fake onoff Gpio class */ const onoffsim = { /** - * Gpio() - * Returns a Gpio class in the case of running on a dev machine * * @param {integer} no Number of the GPIO pin diff --git a/lib/wifi/Readme.md b/lib/wifi/Readme.md index 7d4ef84..af31581 100644 --- a/lib/wifi/Readme.md +++ b/lib/wifi/Readme.md @@ -17,7 +17,6 @@ Class representing the wifi features ### wifi.list(callback) -Wifi.list() - List available wifi access points **Kind**: instance method of [Wifi](#Wifi) @@ -29,7 +28,6 @@ List available wifi access points ### wifi._readConfigCb(err, data) -Wifi._readConfigCb() - (internal function) Invoked after config file is read, then invokes file write on the config file @@ -43,7 +41,6 @@ then invokes file write on the config file ### wifi._writeConfigCb(err) -Wifi._writeConfigCb() - (internal function) Invoked after config file is written, then executes reconfiguration command @@ -56,7 +53,6 @@ then executes reconfiguration command ### wifi._reconfigureCb(err, stdout, stderr) -Wifi._reconfigureCb() - (internal function) Invoked after reconfiguration command is complete **Kind**: instance method of [Wifi](#Wifi) @@ -70,7 +66,6 @@ Wifi._reconfigureCb() - ### wifi._refreshCb(err, stdout, stderr) -Wifi._refreshCb() - (internal function) Invoked after wifi refresh command is complete **Kind**: instance method of [Wifi](#Wifi) @@ -84,7 +79,6 @@ Wifi._refreshCb() - ### wifi.setNetwork(ssid, pwd, callback) -Wifi.setNetwork() - Function which initializes the processes for adding a wifi access point authentication **Kind**: instance method of [Wifi](#Wifi) @@ -98,7 +92,6 @@ Function which initializes the processes for adding a wifi access point authenti ### wifi.getNetwork(callback) -Wifi.getNetwork() - Executes command which gets the currently connected network **Kind**: instance method of [Wifi](#Wifi) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 22096b6..513062a 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -20,7 +20,6 @@ class Wifi { this._ssid = null } /** - * Wifi.list() - * List available wifi access points * * @param {function} callback Function which gets invoked after list is returned @@ -45,7 +44,6 @@ class Wifi { }) } /** - * Wifi._readConfigCb() - * (internal function) Invoked after config file is read, * then invokes file write on the config file * @@ -66,7 +64,6 @@ class Wifi { fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** - * Wifi._writeConfigCb() - * (internal function) Invoked after config file is written, * then executes reconfiguration command * @@ -80,7 +77,6 @@ class Wifi { exec(reconfigure, this._reconfigureCb) } /** - * Wifi._reconfigureCb() - * (internal function) Invoked after reconfiguration command is complete * * @param {object} err (optional) Error object only present if configuration command fails @@ -96,7 +92,6 @@ class Wifi { exec(refresh, this._refreshCb) } /** - * Wifi._refreshCb() - * (internal function) Invoked after wifi refresh command is complete * * @param {object} err (optional) Error object only present if refresh command fails @@ -113,7 +108,6 @@ class Wifi { this._callback = () => {} } /** - * Wifi.setNetwork() - * Function which initializes the processes for adding a wifi access point authentication * * @param {string} ssid SSID of network to configure @@ -127,7 +121,6 @@ class Wifi { fs.readFile(filePath, 'utf8', this._readConfigCb) } /** - * Wifi.getNetwork() - * Executes command which gets the currently connected network * * @param {function} callback Function which is invoked after command is completed From bc077a24a1f63259cadf2138ba16208e9ae42b30 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 Sep 2017 15:40:42 -0400 Subject: [PATCH 037/414] Document remaining functions in lib/intval --- lib/intval/Readme.md | 21 +++++++++++++++++++++ lib/intval/index.js | 12 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 3d4ed58..c58b565 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -13,7 +13,9 @@ Class representing the intval3 features * [._stop()](#Intval+_stop) * [._watchMicro(err, val)](#Intval+_watchMicro) * [._watchRelease(err, val)](#Intval+_watchRelease) + * [.setDir([dir])](#Intval+setDir) * [.frame([dir], [time], [delay])](#Intval+frame) + * [.sequence()](#Intval+sequence) @@ -87,6 +89,19 @@ Button + 10K ohm resistor | err | object | Error object present if problem reading pin | | val | integer | Current value of the pin | + + +### intval.setDir([dir]) +Set the default direction of the camera. +* forward = true +* backward = false + +**Kind**: instance method of [Intval](#Intval) + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [dir] | boolean | true | Direction of the camera | + ### intval.frame([dir], [time], [delay]) @@ -100,3 +115,9 @@ Begin a single frame with set variables or defaults | [time] | integer | "null" | (optional) Exposure time, 0 = minimum | | [delay] | integer | "null" | (optional) Delay after frame before another can be started | + + +### intval.sequence() +Start a sequence of frames, using defaults or explicit instructions + +**Kind**: instance method of [Intval](#Intval) diff --git a/lib/intval/index.js b/lib/intval/index.js index b0b7c4f..35cb4ea 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -177,6 +177,14 @@ class Intval { } } } + /** + * Set the default direction of the camera. + * * forward = true + * * backward = false + * + * @param {boolean} [dir=true] Direction of the camera + * + */ setDir (val = true) { if (typeof val !== 'boolean') { return console.warn('Direction must be represented as either true or false') @@ -231,6 +239,10 @@ class Intval { } } } + /** + * Start a sequence of frames, using defaults or explicit instructions + * + */ sequence () { console.log(`Started sequence`) } From aaef150f8a9a9f352bca68229d14ba65a089e1e0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 22 Sep 2017 00:29:31 -0400 Subject: [PATCH 038/414] Begin microswitch test logic --- tests/gpio.js | 100 +++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index ac46e7c..ccd8e5c 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -6,44 +6,70 @@ const btn = Gpio(5, 'in', 'both') console.log('Watching input on GPIO 05') -let saveTime = 0 -let active = false -btn.watch((err, val) => { - const NOW = +new Date() - /* Button + 10K ohm resistor */ - /* 1 = open */ - /* 0 = closed */ - if (err) { - return console.error(err) - } - console.log(`Release switch val: ${val}`) - if (val === 1) { - //console.log('open') - } else if (val === 0) { - //console.log('closed') - } - if (val === 0) { - if ((!active && saveTime === 0) || (active && NOW - saveTime > 10 * 1000)) { - saveTime = NOW - active = true //maybe unncecessary - } else { - //saveTime = 0 - //active = false +function releaseTest () { + let saveTime = 0 + let active = false + btn.watch((err, val) => { + const NOW = +new Date() + /* Button + 10K ohm resistor */ + /* 1 = open */ + /* 0 = closed */ + if (err) { + return console.error(err) } - } else if (val === 1) { - if (active) { - if (NOW - saveTime > 50 && NOW - saveTime < 1000) { - console.log('Started Frame') - } else if (NOW - saveTime >= 1000) { - console.log('Started Sequence') + console.log(`Release switch val: ${val}`) + if (val === 0) { + //console.log('closed') + } else if (val === 1) { + //console.log('open') + } + if (val === 0) { + //closed + if ((!active && saveTime === 0) || (active && NOW - saveTime > 10 * 1000)) { + saveTime = NOW + active = true //maybe unncecessary + } else { + //saveTime = 0 + //active = false + } + } else if (val === 1) { + //open + if (active) { + if (NOW - saveTime > 50 && NOW - saveTime < 1000) { + console.log('Started Frame') + } else if (NOW - saveTime >= 1000) { + console.log('Started Sequence') + } + console.log(`Release closed for ${NOW - saveTime}`) + saveTime = 0 + active = false } - console.log(`Release closed for ${NOW - saveTime}`) - saveTime = 0 - active = false } - } -}) + }) +} -/*setInterval(() => { - console.log(btn.readSync()) -}, 1000)*/ \ No newline at end of file +function microTest () { + let saveTime = 0 + let active = false //this._state.active + let primed = false //this._state.primed + btn.watch((err, val) => { + const NOW = +new Date() + if (err) { + return console.error(err) + } + console.log(`Micro switch val: ${val}`) + if (val === 0) { + //console.log('closed') + } else if (val === 1) { + //console.log('open') + } + if (val === 0) { + //console.log('closed') + } else if (val === 1) { + //console.log('open') + } + }) +} + +//releaseTest() +microTest() \ No newline at end of file From 31a5b315bd59231f42201a55806fe4b7ee3918ba Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 08:53:48 -0400 Subject: [PATCH 039/414] Try out microswitch logic --- lib/intval/index.js | 21 ++++++++++++++------- tests/gpio.js | 29 ++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 35cb4ea..b3659f3 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -37,16 +37,20 @@ class Intval { this._state = { dir : true, //forward frame : { - start : 0, - active : false, - time : 0, - delay : 0, - val : 0, - expected : 0 + start : 0, //time frame started, timestamp + active : false, //should frame be running + time : 0, //length of frame, in ms + delay : 0, //delay before start of frame, in ms + + expected : 0 //expected length of frame, in ms }, release : { time: 0, - active : false + active : false //is pressed + }, + micro : { + time : 0, + primed : false //is ready to stop frame } } this._releaseMin = 50 @@ -110,6 +114,7 @@ class Intval { this._pin.micro.unwatch() this._state.frame.active = false + this._state.frame.start = 0 } /** * Callback for watching relese switch state changes. @@ -158,12 +163,14 @@ class Intval { } console.log(`Release switch val: ${val}`) if (val === 0) { + //closed if ((!this._state.release.active && this._state.release.time === 0) || (this._state.release.active && (NOW - this._state.release.time) > (this._releaseSequence * 10)) ) { this._state.release.time = NOW this._state.release.active = true //maybe unncecessary } } else if (val === 1) { + //opened if (this._state.release.active) { press = NOW - this._state.release.time if (press > this._releaseMin && press < this._releaseSequence) { diff --git a/tests/gpio.js b/tests/gpio.js index ccd8e5c..46342d3 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -2,11 +2,10 @@ const Gpio = require('onoff').Gpio -const btn = Gpio(5, 'in', 'both') - -console.log('Watching input on GPIO 05') - function releaseTest () { + const PIN = 5 + const btn = Gpio(PIN, 'in', 'both') + console.log(`Watching input on GPIO 0${PIN}`) let saveTime = 0 let active = false btn.watch((err, val) => { @@ -49,8 +48,11 @@ function releaseTest () { } function microTest () { + const PIN = 5 + const btn = Gpio(PIN, 'in', 'both') + console.log(`Watching input on GPIO 0${PIN}`) let saveTime = 0 - let active = false //this._state.active + let frameActive = true //this._state.frame.active let primed = false //this._state.primed btn.watch((err, val) => { const NOW = +new Date() @@ -63,10 +65,19 @@ function microTest () { } else if (val === 1) { //console.log('open') } - if (val === 0) { - //console.log('closed') - } else if (val === 1) { - //console.log('open') + if (val === 0 && frameActive) { + if (!primed) { + primed = true + saveTime = NOW + console.log('Primed') + } + } else if (val === 1 && frameActive) { + if (primed) { + primed = false + setTimeout( () => { + console.log(`Stop Frame after ${NOW - saveTime}`) + }, 10) + } } }) } From f419bcd36ee8675bb2583da6ef593b131c68c7cc Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 08:56:29 -0400 Subject: [PATCH 040/414] Test microswitch on pin 6 --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 46342d3..3d1812d 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -48,7 +48,7 @@ function releaseTest () { } function microTest () { - const PIN = 5 + const PIN = 6 const btn = Gpio(PIN, 'in', 'both') console.log(`Watching input on GPIO 0${PIN}`) let saveTime = 0 From bde21e9ac75c554cdf8ec85681df2b9444314c8d Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 17:56:13 -0400 Subject: [PATCH 041/414] Test setting output pin high --- tests/gpio.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 3d1812d..6519f3f 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -82,5 +82,18 @@ function microTest () { }) } +//test stepping up of 3.3V RPI logic via +//Sparkfun PRT-10967 (NPC1402) +function stepupTest () { + const FWD = 13 + const BWD = 19 + const fwd = Gpio(FWD, 'out') + const bwd = Gpio(BWD, 'out') + + console.log(`Setting pin ${FWD} high`) + fwd.set(1) +} + //releaseTest() -microTest() \ No newline at end of file +//microTest() +stepupTest() \ No newline at end of file From ef1a793a08fa3df11c3ec51689efa0b5574ca0d6 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 17:58:16 -0400 Subject: [PATCH 042/414] WriteSync, not set --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 6519f3f..36a0e5a 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -91,7 +91,7 @@ function stepupTest () { const bwd = Gpio(BWD, 'out') console.log(`Setting pin ${FWD} high`) - fwd.set(1) + fwd.writeSync(1) } //releaseTest() From 68e49e2e220b1101c4839c7bbfc9a9f4c40e8045 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 17:59:55 -0400 Subject: [PATCH 043/414] Test all together, watch and writeSync --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 36a0e5a..633a120 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -94,6 +94,6 @@ function stepupTest () { fwd.writeSync(1) } -//releaseTest() -//microTest() +releaseTest() +microTest() stepupTest() \ No newline at end of file From d7671b15aba9f73e1dea8b9f8d9c5dc243437907 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 18:59:24 -0400 Subject: [PATCH 044/414] Using pin 33 instead of 13 Per this thread https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=71247 --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 633a120..3c1a705 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -85,7 +85,7 @@ function microTest () { //test stepping up of 3.3V RPI logic via //Sparkfun PRT-10967 (NPC1402) function stepupTest () { - const FWD = 13 + const FWD = 33 // RPIO PIN 13 const BWD = 19 const fwd = Gpio(FWD, 'out') const bwd = Gpio(BWD, 'out') From 8f8785c4fb844e4559191e867889c8332a4fd4d3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 19:02:48 -0400 Subject: [PATCH 045/414] No idea why I believed that comment Trying gpis 18 --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 3c1a705..1e1cb8f 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -85,7 +85,7 @@ function microTest () { //test stepping up of 3.3V RPI logic via //Sparkfun PRT-10967 (NPC1402) function stepupTest () { - const FWD = 33 // RPIO PIN 13 + const FWD = 18 // RPIO PIN 13 const BWD = 19 const fwd = Gpio(FWD, 'out') const bwd = Gpio(BWD, 'out') From 31deef9ada1cd13190d91c462487270988fcb344 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 23 Sep 2017 19:06:32 -0400 Subject: [PATCH 046/414] Still reading 1.6V on GPIO pin set high Possibly damaged pin 18 --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 1e1cb8f..99f9fad 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -85,7 +85,7 @@ function microTest () { //test stepping up of 3.3V RPI logic via //Sparkfun PRT-10967 (NPC1402) function stepupTest () { - const FWD = 18 // RPIO PIN 13 + const FWD = 13 // RPIO PIN 13 const BWD = 19 const fwd = Gpio(FWD, 'out') const bwd = Gpio(BWD, 'out') From ef130b3939317fea3a2263f6ca2f08e8b982b9d5 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 15:06:04 -0400 Subject: [PATCH 047/414] Set motor pins to low on SIGINT --- tests/gpio.js | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 99f9fad..78cd34b 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -2,13 +2,29 @@ const Gpio = require('onoff').Gpio +let release +let micro +let fwd +let bwd + +process.on('SIGINT', () => { + if (fwd && fwd.writeSync) { + console.log(`Setting fwd to 0`) + fwd.writeSync(0) + } + if (bwd && bwd.writeSync) { + console.log(`Setting bwd to 0`) + bwd.writeSync(0) + } +}) + function releaseTest () { - const PIN = 5 - const btn = Gpio(PIN, 'in', 'both') + const PIN = 6 + release = Gpio(PIN, 'in', 'both') console.log(`Watching input on GPIO 0${PIN}`) let saveTime = 0 let active = false - btn.watch((err, val) => { + release.watch((err, val) => { const NOW = +new Date() /* Button + 10K ohm resistor */ /* 1 = open */ @@ -48,13 +64,13 @@ function releaseTest () { } function microTest () { - const PIN = 6 - const btn = Gpio(PIN, 'in', 'both') + const PIN = 5 + micro = Gpio(PIN, 'in', 'both') console.log(`Watching input on GPIO 0${PIN}`) let saveTime = 0 let frameActive = true //this._state.frame.active let primed = false //this._state.primed - btn.watch((err, val) => { + micro.watch((err, val) => { const NOW = +new Date() if (err) { return console.error(err) @@ -83,17 +99,18 @@ function microTest () { } //test stepping up of 3.3V RPI logic via -//Sparkfun PRT-10967 (NPC1402) +//Sparkfun PRT-10968 (NPC1402) function stepupTest () { const FWD = 13 // RPIO PIN 13 const BWD = 19 - const fwd = Gpio(FWD, 'out') - const bwd = Gpio(BWD, 'out') + fwd = Gpio(FWD, 'out') + bwd = Gpio(BWD, 'out') console.log(`Setting pin ${FWD} high`) fwd.writeSync(1) + bwd.writeSync(1) } releaseTest() microTest() -stepupTest() \ No newline at end of file +stepupTest() From 26f3ed4a11235dc01c9a24652a0c01512dbc5588 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 16:05:35 -0400 Subject: [PATCH 048/414] Set bwd to low --- tests/gpio.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 78cd34b..dc61de8 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -16,6 +16,7 @@ process.on('SIGINT', () => { console.log(`Setting bwd to 0`) bwd.writeSync(0) } + process.exit() }) function releaseTest () { @@ -108,7 +109,7 @@ function stepupTest () { console.log(`Setting pin ${FWD} high`) fwd.writeSync(1) - bwd.writeSync(1) + bwd.writeSync(0) } releaseTest() From 50262facc2b720bf9297d3cba80274aa0d2a5ac9 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 22:20:26 -0400 Subject: [PATCH 049/414] Intval 3 board prototype Design roughed in fritzing, the pdf pulled into photoshop and hand routed. --- hardware/intval3_0_prototype.psd | Bin 0 -> 651909 bytes hardware/intval3_0_prototype_mask.png | Bin 0 -> 138890 bytes hardware/intval_3_prototype.fzz | Bin 0 -> 9395 bytes .../intval_3_prototype_etch_copper_top.pdf | Bin 0 -> 4756 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 hardware/intval3_0_prototype.psd create mode 100644 hardware/intval3_0_prototype_mask.png create mode 100644 hardware/intval_3_prototype.fzz create mode 100644 hardware/intval_3_prototype_etch_copper_top.pdf diff --git a/hardware/intval3_0_prototype.psd b/hardware/intval3_0_prototype.psd new file mode 100644 index 0000000000000000000000000000000000000000..8743617f737dac2509f634f3595a0633c9371d59 GIT binary patch literal 651909 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
  • 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�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 literal 0 HcmV?d00001 diff --git a/hardware/intval3_0_prototype_mask.png b/hardware/intval3_0_prototype_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..fe7899407b39047de332bda377ebea55f8dc8b55 GIT binary patch literal 138890 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 Cg|1Hk literal 0 HcmV?d00001 diff --git a/hardware/intval_3_prototype.fzz b/hardware/intval_3_prototype.fzz new file mode 100644 index 0000000000000000000000000000000000000000..6eab7573975bae83fc4bd26b6e0171890d1d9a3a GIT binary patch literal 9395 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 literal 0 HcmV?d00001 diff --git a/hardware/intval_3_prototype_etch_copper_top.pdf b/hardware/intval_3_prototype_etch_copper_top.pdf new file mode 100644 index 0000000000000000000000000000000000000000..02acaa28718f20c048f6c08f0edc53308a4de559 GIT binary patch literal 4756 zcmZWt3p~^78+Xd-XiCFT$!!Z2Vl&%}P?<}`B9sv2GR$FPn_N>Vw-OSOxg^JBG!hND zb&-i7$7Q)iCJQ0=iunJgROf%T&pyB3`@GNdyx-^h-u66-<_1Qp>S{=kIs^_OIeCC| zbU-jG0@V`_*|)N)1cG77ctWRAWmjFcJu6c^E<^`|@9K5V#r~0fIR@Le$mZ0Q|$>o`;WGLR=j^DQh~8 zl)oUb<0K;fCmz&y_jbVWR4SfKguo2&=Lybu3uArzb>hNz(9NvZCs88~T*zpAjG#sLif!qHQI^S%4 zYOo|v64}z*(HRfn&y+dV(i_6ZZUKY}y#55e)^=ML7?T|X_#REwseH>iCrW5 zN92HGzN!%r2VaZ-e%j2*1Mj@v{7Ktd418>MkRL4Z!(NALiYK~J-65E72#pAy0JRX9 zktZ-~26$(Z3!d+tLIu{C7sx+#BCWZQTQ3^67znDqG`ixvdcRr-4Eb$S$(Dyg#8knj z`ywX{9S-*Ytv0SgcZv0vAlxjzA=$5?8_~(LDo$~+Tv_qUSS2qn!B|rlKP^vjdp|GA z>B;pCKeSubin&-TKe)85=ElVsy)t^@J8tjj$M)&m=fHPm{L_WyFD;c1muD7=o-dAz zZX@JhVRHga+jSOWvrD<7U^`mw;PRQsi#F59RqYz{E-+G-ykOZ(NH6*b57-v0Tlh?r z_j`@t+aPdlXp_(70*pjVK=+qmtz|CUjQClK5kG3MJk*f-)JunUtgk-PJtc)ccOYbf zUHKdxl77tXAD=hRBw`t6){~#gZl>}2J`i+NM?YLY7~;3`!D*ae%R|3NaJp}t;gNc5 z3uV{mYS(F=+={=@=%ftyk`Zqeeuz9AqCUzmy%lY>U2mCoXLncQ-tgBB= zt%m%wbW}~Vvs=(Dd!=S=O0Q!sEnpNpVs1;ucXx0ZJKDt4gtCvT#faye-(8A8WvHb_ z>Q%>vZ#a}F{v=a1IqH~nV-%c2|Kq_<6*lyDd6Bz~n`7#?JoOx#?mYf!XM_Y%1g7+I zoAip^QN2D?R7hNC+@4`y`?s$e%Q>ZKn7kR~E+3rg6z%vRCA_X1*T#@^c=qSW zgI)Snx0NDN?6vAo=cGLQ^g8xH1v5GZYO2J;i-p$6d>NQJ`04eA=~qR)lya9n%05*x zk*xvw*;};XO9?r{Fs?ukH!e?Wmxf>ooh82YsFGRc3l(hSbG_0FSoqqYn|Q71ew;>6n7eLQ${FQ4%2Z-E>;9c63!$Owp2Sy|1CB`BNo@rG@v3Yi zAG3L~mu|ZIksDc2?+QRrwSY6#ogD=wv_SeP%c;Mzj{ChdP0$A?>1@&<%lhPje!)zD z(sK6??Y84Q!QeYxHRo*^Id3f6FnCdZh<1-sw1>>YAs&ONneg5~oi^~4{%K2nDg84p zvn^>$@`|$C?%oFxUU-bDx@GA{0@&ghr*Seq`>AJ zpkEPQ)OPh#l0){A)tWDIjCpOjusfFl(*!T)Q(`0^cPy}Yzb!yhY^a?5{(2MaC6n>_ zr}HbXS!~bt3nJCD+kRMP&SX$@_2TxVx%UBcqi2vB=6E4*LiIT#nvrxqUH+#Fr?V>c zO>7+{=>Q$#`3|ZL^lKj6Kv`k9EeNKfz^*ns>p&Je87^QfE383r7$WTYi)~vzc(8%{`rZj>aS@IIiqhI8ftWmg##)F!+;A~mgQqBB4;4chJBOb`&->>< zDQ8t|B{oP0qz^1BQ`i0i718(;nA%+oW9udkM=t zV;oe4R9JjO$mfI)cQ1-}W<$A?2FI=0p~^rv+A*h2ECOwC?^G5(+=V#@V9V~t$zWVm zfay}~#Z@a5&|?8ux%=-nf(PSp$x6j_nOkq>b-s5+`SPQ;DCqCFoHgI&gqqg|)c1T- z$I0X&1TnTn@DiMf^kce7hX?gh%40f9V=JMPIQHZw>w>aTSZOzLOw|^5p)akx5X^Vq z;Oywy0_f{2c%UBJ;`%$dQQ?A|q-&WD{Y|@Lc!6qCINkCZByM!9>!^2|`

    2YQy9; z8}s=(aCyU~@4LqK2LDX^hkGY{u6>#s(r|yvZzA2n=#;?PauhA_y|oPoryETZ*K$x7 z&gXYsy(4BK-2;wVw?l;|=!B)V_TnyS4kT$o7q0#dwU23&m{LqTPi3mmo(lVU@4x5!sq;_NQ*lDa&s&WDpQ|vu=Ud)0mmA_Rq z_2Fg>#oL%tsMw=j)y?MWc7atnbpAY{nZ`Ghn-jtx%#-K3awKNMm7#!e{qXRZLt|ff zj!viGi_iui4Vkxj<`p086=eUgZgRNm(OqCf61D+w^?;v7c%l|mz^(YHIR6iCy4HI& ztk}0@iz?t9;X*As^@`icvO8Sz^}RpWxAkVHJH0XYiX3GBLjHOj3~`_%VXGz-qE58g zhxNX4C98}Ey%vCd1P!(PW$BbjD!ZD*a{A5y{*QNi(?rC$Iq3+&G6Ai?O8dNIO)EYf z;;zT`ukG6(G)*iDV^cdjeB`CXxXdzypyo?0Z5y0DA5FrKm6ix?@fgdIEebf44baEf z5gYg_XZqX`l&_A_(EDzWu}}}Jpxd~FE0Yj~)%1-@;jd$0k2TAmSqn;Mi(w15jh+wI zaewzi{g+)_%8mmL@}lnHyMXtA=!p#t0iJ5+5}^zs>1r=KNXcD*(>ttZ?9?O-HS9=- zw5o;zaBKX<{EYw%E9;EiN|x;P8VC`km)fYgVMmN`e*s;S7U zQhMsP=!j6sJh2TJmyH?8pO`9!X?A5@ zdF#z@R|)=`a*_N#?4_)EvU6=Ip=Q&QoR*|bf-K=p=QibE6^Q8)b}c2;Wu|BYcf}nj za923YK8peN@l0}2&5@Q_L&;M@XA4B7iv};Pd7s(IZV0H$R2DozDFv=WCNM|YI6m;V z@8Cy1OysoeXcc6cc&zyr>3*^$N;CqorWMDiI}%z}8#TqL z?R#!`uqH&UV%~!ubN%&OE1E^V?*%J0xh30yy|qSdfAHq@Kk($XaSJ%IGWv_zyurYi zpA(_MhoVM;{-nRCs45_WOwNO;KZ zI-lX(;?N{_K{-+3Tj{OV8SfT^cP@v83PZ#fCv-ioamT~LorN2eFYmgY5UFRMS1y1E zh>twfRGcS|6$_5CD_0wt7gP)qZ#$-TiLDoC@)F;>H*rg+1;QmQ<}*UB#3Ei{cN=-X z^2<2*eq}}2$o!*Y3x$>YYg)lD-T>En3*vf#!yq5$k*1n`y1(avgT31*wcB4`t z)AVJ=Vo%JJjqO5Zn)aIuwwzmEZJo4>8f)iX)udNmtHd`0(rE`cbcM3|wA|C}B9jgS(2QD1*AJ3W zQ$BXH6|ub$5B{9QOtnPz?rpaX3NE-&j?+7g>Hqll_+)a3<*oieT6!8$dRA|8u;9+X(N8>3kCC6&9 zdRSFSz1n66N2HrjB9u7)5AXYzjQ+rxSfDm**GFx$+V#@D}H z(b$YUVeQU5wNw#+a+0Hz?NKJ4Pwns96EX6pIuNdf^qT@NE?g;=RpDu7vrP0>l$XvI z6j$MFXDhZhbv(;A@_zUHLiEWA()|j#wu>j_X62?(nn6qQfeJd79Oyik=NUAaE5+)o zC7L@lrkt-$8c7*FJT9Z5C$soff>7!E_~!K47R~Um0l%JCge&GD+9BG@I}&QdWr2#r z_YCuU?SY?-@)J}Iv?gLbwda=`){|DNwR*%_83F_|A-GT=AUhC0_qPDq18FtM7s!j% zf!v!+a<;@%AwbE%zz71f!uwMpu%li;ML_@i6Z>lx?@z|Nf`Hs!9R&aJfuK<+4HU!` z@>2#R`}_}t_(P_S0O|t&mLWBOD#8EB;OglA+9J?EdjD@*xCR`k_57aF`m Date: Sun, 24 Sep 2017 22:27:53 -0400 Subject: [PATCH 050/414] Add log library --- lib/log/Readme.md | 13 +++++++++++++ lib/log/index.js | 22 ++++++++++++++++++++++ lib/wifi/index.js | 1 + 3 files changed, 36 insertions(+) create mode 100644 lib/log/Readme.md create mode 100644 lib/log/index.js diff --git a/lib/log/Readme.md b/lib/log/Readme.md new file mode 100644 index 0000000..61f4e44 --- /dev/null +++ b/lib/log/Readme.md @@ -0,0 +1,13 @@ + + +## createLog(label, filename) ⇒ object +createLog() - Returns a winston logger configured to service + +**Kind**: global function +**Returns**: object - Winston logger + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| label | string | | Label appearing on logger | +| filename | string | null | Optional file to write log to | + diff --git a/lib/log/index.js b/lib/log/index.js new file mode 100644 index 0000000..8527559 --- /dev/null +++ b/lib/log/index.js @@ -0,0 +1,22 @@ +'use strict' + +const winston = require('winston') + +/** +* createLog() - Returns a winston logger configured to service +* +* @param {string} label Label appearing on logger +* @param {string} filename Optional file to write log to +* @returns {object} Winston logger +*/ +function createLog (label, filename = null) { + const transports = [ new (winston.transports.Console)({ label : label }) ] + if (filename !== null) { + transports.push( new (winston.transports.File)({ label : label, filename : filename }) ) + } + return new (winston.Logger)({ + transports: transports + }) +} + +module.exports = createLog \ No newline at end of file diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 513062a..2f236d2 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -9,6 +9,7 @@ const refresh = '/sbin/ifdown wlan0 && /sbin/ifup --force wlan0' const iwlist = '/sbin/iwlist wlan0 scanning | grep "ESSID:"' const iwgetid = '/sbin/iwgetid' +const log = require('../log')('wifi') const exec = require('child_process').exec const fs = require('fs') From a9f2040f3f356cd7a2a26036a5115a5ce798081e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 22:28:06 -0400 Subject: [PATCH 051/414] Squash errors on connect (for dev) --- lib/blootstrap/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index 33e7fb0..749c88a 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -23,6 +23,11 @@ class Blootstrap { ipc.of.blootstrap_ble.on('disconnect', () => { ipc.log(`Disconnected from the blootstrap_ble service`) }) + ipc.of.blootstrap_ble.on('error', (err) => { + if (err.code === 'EACCES') { + console.log(`Cannot access ipc`) + } + }) }) } /** From 50a335af95c0b6816126ddfddbba8f6eaa1dcb99 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 22:28:26 -0400 Subject: [PATCH 052/414] Add log library and microswitch logic --- lib/intval/index.js | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index b3659f3..463c1bf 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,5 +1,7 @@ 'use strict' +const log = require('../log')('intval') + let Gpio try { Gpio = require('onoff').Gpio @@ -42,7 +44,7 @@ class Intval { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms - expected : 0 //expected length of frame, in ms + expected : 1000 //expected length of frame, in ms }, release : { time: 0, @@ -55,6 +57,7 @@ class Intval { } this._releaseMin = 50 this._releaseSequence = 1000 + this._microDelay = 10 // delay after stop signal before stopping motors this._declarePins() process.on('SIGINT', this._undeclarePins) } @@ -88,8 +91,8 @@ class Intval { * */ _startFwd () { - this._pin.fwd.set(1) - this._pin.bwd.set(0) + this._pin.fwd.writeSync(1) + this._pin.bwd.writeSync(0) //start high-cpu watch } /** @@ -97,16 +100,16 @@ class Intval { * */ _startBwd () { - this._pin.fwd.set(0) - this._pin.bwd.set(1) + this._pin.fwd.writeSync(0) + this._pin.bwd.writeSync(1) } /** * Stop motor by setting both motor pins to 0 (LOW) * */ _stop () { - this._pin.fwd.set(0) - this._pin.bwd.set(0) + this._pin.fwd.writeSync(0) + this._pin.bwd.writeSync(0) let len = (+new Date()) - this._state.frame.start @@ -119,8 +122,9 @@ class Intval { /** * Callback for watching relese switch state changes. * Using GPIO 06 on Raspberry Pi Zero W. - * * If closed, start timer. - * * If opened, check timer AND + * + * 1) If closed AND frame active, start timer, set state primed to `true`. + * 1) If opened AND frame active, stop frame * * Microswitch + 10K ohm resistor * * 1 === open @@ -132,11 +136,26 @@ class Intval { * */ _watchMicro (err, val) { + const NOW = +new Date() if (err) { console.error(err) } - this._state.frame.val = val //determine when to stop + if (val === 0 && this._state.frame.active) { + if (!this._state.micro.primed) { + this._state.micro.primed = true + this._state.micro.time = NOW + console.log('Mircoswitch primed to stop motor') + } + } else if (val === 1 && this._state.frame.active) { + if (this._state.micro.primed) { + this._state.micro.primed = false + this._state.micro.time = 0 + setTimeout( () => { + console.log(`Stopped frame after ${NOW - this._state.micro.time}ms`) + }, this._microDelay) + } + } } /** * Callback for watching relese switch state changes. @@ -178,7 +197,7 @@ class Intval { } else if (press >= this._releaseSequence) { this.sequence() } - console.log(`Release closed for ${press}`) + console.log(`Release closed for ${press}ms`) this._state.release.time = 0 this._state.release.active = false } From 4ec3cf422b3cdf1c33c9ba76a9e6c20b6bd56fef Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 24 Sep 2017 22:28:46 -0400 Subject: [PATCH 053/414] Document intval lib logic for microswitch watcher --- lib/intval/Readme.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index c58b565..70b8b9f 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -53,8 +53,9 @@ Stop motor by setting both motor pins to 0 (LOW) ### intval._watchMicro(err, val) Callback for watching relese switch state changes. Using GPIO 06 on Raspberry Pi Zero W. -* If closed, start timer. -* If opened, check timer AND + +1) If closed AND frame active, start timer, set state primed to `true`. +1) If opened AND frame active, stop frame Microswitch + 10K ohm resistor * 1 === open From a5b4ef2149eed994126bd30dc0dd0e840f6001e7 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:25:15 -0400 Subject: [PATCH 054/414] Import mscript library from mcopy Refactored into es6 style, intention is to extend it to accept delays in looping logic --- lib/mscript/Readme.md | 35 ++++ lib/mscript/index.js | 441 ++++++++++++++++++++++++++++++++++++++++++ tests/mscript.js | 169 ++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 lib/mscript/Readme.md create mode 100644 lib/mscript/index.js create mode 100644 tests/mscript.js diff --git a/lib/mscript/Readme.md b/lib/mscript/Readme.md new file mode 100644 index 0000000..611eefa --- /dev/null +++ b/lib/mscript/Readme.md @@ -0,0 +1,35 @@ + + +## mscript +Object representing mscript parser + +**Kind**: global constant + +* [mscript](#mscript) + * [.arg(shrt, lng)](#mscript.arg) + * [.arg_pos(shrt, lng)](#mscript.arg_pos) + + + +### mscript.arg(shrt, lng) +Determine whether or not argument flag has been set + +**Kind**: static method of [mscript](#mscript) + +| Param | Type | Description | +| --- | --- | --- | +| shrt | string | Short flag name (ie `-a`) | +| lng | string | Long flag name (ie `--apple`) | + + + +### mscript.arg_pos(shrt, lng) +Determine position of flag, in argument array + +**Kind**: static method of [mscript](#mscript) + +| Param | Type | Description | +| --- | --- | --- | +| shrt | string | Short flag name (ie `-a`) | +| lng | string | Long flag name (ie `--apple`) | + diff --git a/lib/mscript/index.js b/lib/mscript/index.js new file mode 100644 index 0000000..c12700e --- /dev/null +++ b/lib/mscript/index.js @@ -0,0 +1,441 @@ +'use strict' + +let fs +let input; + +/** Object representing mscript parser */ +const mscript = {} + +/** +* Determine whether or not argument flag has been set +* +* +* @param {string} shrt Short flag name (ie `-a`) +* @param {string} lng Long flag name (ie `--apple`) +* +*/ +mscript.arg = function arg (shrt, lng) { + if (process.argv.indexOf(shrt) !== -1 || + process.argv.indexOf(lng) !== -1) { + return true + } + return false +} +/** +* Determine position of flag, in argument array +* +* +* @param {string} shrt Short flag name (ie `-a`) +* @param {string} lng Long flag name (ie `--apple`) +* +*/ +mscript.arg_pos = function arg_pos (shrt, lng) { + let pos = process.argv.indexOf(shrt) + if (pos === -1) { + pos = process.argv.indexOf(lng) + } + return pos +} + +mscript.black = '0,0,0' + +mscript.cmd = [ + 'CF', + 'PF', + 'BF', + 'CB', + 'PB', + 'BB', + 'D' +] +mscript.alts = { + 'CF' : ['CAMERA FORWARD', 'CAM FORWARD', 'C'], + 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'P'], + 'BF' : ['BLACK FORWARD'], + 'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'], + 'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'], + 'BB' : ['BLACK BACKWARD', 'BLACK BACK'], + 'L ' : ['LIGHT', 'COLOR', 'LAMP'] +} + +mscript.state = {} +//TODO: This will memory leak +mscript.state_clear = function state_clear () { + mscript.state = { + cam : 0, + proj : 0, + color : '', + loops : [], + rec : -1 + } +} + +mscript.alts_unique = function alts_unique () { + const ids = Object.keys(mscript.alts) + const all = [] + for (let i = 0; i < ids.length; i++) { + if (all.indexOf(ids[i]) === -1) { + all.push(ids[i]) + } else { + mscript.fail(1, "Can't parse") + } + } +} + +mscript.interpret = function interpret (text, callback) { + mscript.state_clear() + if (typeof text === 'undefined') { + mscript.fail(2, 'No input') + } + const lines = text.split('\n') + const arr = [] + const light = [] + const output = {} + + let two = '' + let target = 0 + let dist = 0 //? + let x + + //loop through all lines + for (let line of lines) { + //preprocess line + line = mscript.preprocess(line) + two = line.substring(0, 2) + + if (mscript.cmd.indexOf(two) !== -1) { + if (mscript.state.loops.length > 0) { + //hold generated arr in state loop array + mscript.state.loops[mscript.state.rec].arr.push.apply(mscript.state.loops[mscript.state.rec].arr, mscript.str_to_arr(line, two)) + mscript.state.loops[mscript.state.rec].light.push.apply(mscript.state.loops[mscript.state.rec].light, mscript.light_to_arr(line, two)) + } else { + arr.push.apply(arr, mscript.str_to_arr(line, two)) + light.push.apply(light, mscript.light_to_arr(line, two)) + } + + } else if (line.substring(0, 4) === 'LOOP') { + mscript.state.rec++ + mscript.state.loops[mscript.state.rec] = { + arr : [], + light : [], + cam : 0, + proj : 0, + cmd : line + '' + } + } else if (line.substring(0, 2) === 'L ') { + mscript.light_state(line) + } else if (line.substring(0, 3) === 'END') { + for (x = 0; x < mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd); x++) { + if (mscript.state.rec === 0) { + arr.push.apply(arr, mscript.state.loops[mscript.state.rec].arr); + light.push.apply(light, mscript.state.loops[mscript.state.rec].light); + } else if (mscript.state.rec >= 1) { + mscript.state.loops[mscript.state.rec - 1].arr.push.apply(mscript.state.loops[mscript.state.rec - 1].arr, mscript.state.loops[mscript.state.rec].arr) + mscript.state.loops[mscript.state.rec - 1].light.push.apply(mscript.state.loops[mscript.state.rec - 1].light, mscript.state.loops[mscript.state.rec].light) + } + } + mscript.state_update('END', mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd)); + delete mscript.state.loops[mscript.state.rec] + mscript.state.rec-- + } else if (line.substring(0, 3) === 'CAM') { //directly go to that frame (black?) + target = parseInt(line.split('CAM ')[1]) + if (mscript.state.loops.length > 0) { + if (target > mscript.state.cam) { + dist = target - mscript.state.cam + for (x = 0; x < dist; x++) { + mscript.state.loops[mscript.state.rec].arr.push('BF') + mscript.state.loops[mscript.state.rec].light.push(mscript.black) + mscript.state_update('BF') + } + } else { + dist = mscript.state.cam - target + for (x = 0; x < dist; x++) { + mscript.state.loops[mscript.state.rec].arr.push('BB') + mscript.state.loops[mscript.state.rec].light.push(mscript.black) + mscript.state_update('BB') + } + } + } else { + if (target > mscript.state.cam) { + dist = target - mscript.state.cam + for (x = 0; x < dist; x++) { + arr.push('BF') + light.push(mscript.black) + mscript.state_update('BF') + } + } else { + dist = mscript.state.cam - target + for (x = 0; x < dist; x++) { + arr.push('BB') + light.push(mscript.black) + mscript.state_update('BB') + } + } + } + } else if (line.substring(0, 4) === 'PROJ') { //directly go to that frame + target = parseInt(line.split('PROJ ')[1]) + if (mscript.state.loops.length > 0) { + if (target > mscript.state.proj) { + dist = target - mscript.state.proj + for (x = 0; x < dist; x++) { + mscript.state.loops[mscript.state.rec].arr.push('PF') + mscript.state.loops[mscript.state.rec].light.push('') + mscript.state_update('PF') + } + } else { + dist = mscript.state.proj - target + for (x = 0; x < dist; x++) { + mscript.state.loops[mscript.state.rec].arr.push('PB') + mscript.state.loops[mscript.state.rec].light.push('') + mscript.state_update('PB') + } + } + } else { + if (target > mscript.state.proj) { + dist = target - mscript.state.proj + for (x = 0; x < dist; x++) { + arr.push('PF') + light.push('') + mscript.state_update('PF') + } + } else { + dist = mscript.state.proj - target + for (x = 0; x < dist; x++) { + arr.push('PB') + light.push('') + mscript.state_update('PB'); + } + } + } + } else if (line.substring(0, 3) === 'SET') { //set that state + if (line.substring(0, 7) === 'SET CAM') { + mscript.state.cam = parseInt(line.split('SET CAM')[1]); + } else if (line.substring(0, 8) === 'SET PROJ') { + mscript.state.proj = parseInt(line.split('SET PROJ')[1]); + } + } else if (line.substring(0, 1) === '#' || line.substring(0, 2) === '//') { + //comments + //ignore while parsing + } + } + output.success = true + output.arr = arr + output.light = light + output.cam = mscript.state.cam + output.proj = mscript.state.proj + if (typeof callback !== 'undefined') { + //should only be invoked by running mscript.tests() + callback(output) + } else { + return mscript.output(output) + } +} + +mscript.preprocess = function preprocess (line) { + line = line.replace(/\t+/g, '') //strip tabs + line = line.trim() //remove excess whitespace before and after command + line = line.toUpperCase() + return line +} + +mscript.last_loop = function last_loop () { + return mscript.state.loops[mscript.state.loops.length - 1] +} + +mscript.parent_loop = function parent_loop () { + return mscript.state.loops[mscript.state.loops.length - 2] +} + +mscript.state_update = function state_update (cmd, val) { + if (cmd === 'END') { + for (var i = 0; i < val; i++) { + if (mscript.state.rec === 0) { + mscript.state.cam += mscript.state.loops[mscript.state.rec].cam + mscript.state.proj += mscript.state.loops[mscript.state.rec].proj + } else if (mscript.state.rec >= 1) { + mscript.state.loops[mscript.state.rec - 1].cam += mscript.state.loops[mscript.state.rec].cam + mscript.state.loops[mscript.state.rec - 1].proj += mscript.state.loops[mscript.state.rec].proj + } + } + } else if (cmd === 'CF') { + if (mscript.state.loops.length < 1) { + mscript.state.cam++ + } else { + mscript.state.loops[mscript.state.rec].cam++ + } + } else if (cmd === 'CB') { + if (mscript.state.loops.length < 1) { + mscript.state.cam-- + } else { + mscript.state.loops[mscript.state.rec].cam-- + } + } else if (cmd === 'PF') { + if (mscript.state.loops.length < 1) { + mscript.state.proj++ + } else { + mscript.state.loops[mscript.state.rec].proj++ + } + } else if (cmd === 'PB') { + if (mscript.state.loops.length < 1) { + mscript.state.proj-- + } else { + mscript.state.loops[mscript.state.rec].proj-- + } + } else if (cmd === 'BF') { + if (mscript.state.loops.length < 1) { + mscript.state.cam++ + } else { + mscript.state.loops[mscript.state.rec].cam++ + } + } else if (cmd === 'BB') { + if (mscript.state.loops.length < 1) { + mscript.state.cam-- + } else { + mscript.state.loops[mscript.state.rec].cam++ + } + } else if (cmd === 'L ') { + //TODO : ???? + } +} + +mscript.str_to_arr = function str_to_arr (str, cmd) { + const cnt = str.split(cmd) + let arr = [] + let c = parseInt(cnt[1]) + + if (cnt[1] === '') { + c = 1 + } else { + c = parseInt(cnt[1]) + } + for (var i = 0; i < c; i++) { + arr.push(cmd) + mscript.state_update(cmd) + } + return arr +} + +mscript.light_state = function light_state (str) { + //add parsers for other color spaces + const color = str.replace('L ', '').trim() + mscript.state.color = color +} + +mscript.light_to_arr = function light_to_arr (str, cmd) { + const cnt = str.split(cmd) + const arr = [] + let c = parseInt(cnt[1]) + + if (cnt[1] === '') { + c = 1 + } else { + c = parseInt(cnt[1]) + } + for (var i = 0; i < c; i++) { + if (cmd === 'CF' || cmd === 'CB') { + arr.push(mscript.state.color) + } else if (cmd === 'BF' || cmd === 'BB') { + arr.push(mscript.black) + } else { + arr.push('') + } + } + return arr +} + +mscript.loop_count = function loop_count (str) { + return parseInt(str.split('LOOP ')[1]) +} + +mscript.fail = function fail (code, reason) { + const obj = { success: false, error: true, msg : reason } + console.error(JSON.stringify(obj)) + if (process) process.exit() +} + +mscript.output = function output (data) { + let json = true; //default + if (mscript.arg('-j', '--json')) { + json = true + } + + if (mscript.arg('-t', '--text')) { + json = false + } + + if (json) { + console.log(JSON.stringify(data)) + } else { + var ids = Object.keys(data) + for (var i = 0; i < ids.length; i++) { + console.log(ids[i] + ': ' + data[ids[i]]) + } + } +} + +mscript.init = function init () { + if (mscript.arg('-t', '--tests')) { + return mscript.tests() + } + + if (mscript.arg('-v', '--verbose')) { + console.time('mscript') + } + + if (mscript.arg('-c', '--cam')) { + mscript.state.cam = parseInt(process.argv[mscript.arg_pos('-c', '--cam') + 1]) + } + + if (mscript.arg('-p', '--proj')) { + mscript.state.proj = parseInt(process.argv[mscript.arg_pos('-p', '--proj') + 1]) + } + + if (mscript.arg('-f', '--file')) { + input = process.argv[mscript.arg_pos('-f', '--file') + 1] + mscript.interpret(fs.readFileSync(input, 'utf8')) + } else { + mscript.interpret(input) + } + + if (mscript.arg('-v', '--verbose')) { + console.timeEnd('mscript') + } +}; + +if (typeof document === 'undefined' && typeof module !== 'undefined' && !module.parent) { + //node script + fs = require('fs') + input = process.argv[2] + mscript.init() +} else if (typeof module !== 'undefined' && module.parent) { + //module + fs = require('fs') + module.exports = mscript +} else { + //web +} + + +/* + +CAM # - go to camera frame # +PROJ # - go to projector frame # + +SET CAM # - sets camera count to # +SET PROJ # - sets projector count to # + +LOOP # - begin loop, can nest recursively, # times +END LOOP - (or END) closes loop + +L #RGB - sets light to rgb value + +FADE + +CF - Camera forwards +PF - Projector forwards +BF - Black forwards +CB - Camera backwards +PB - Projector backwards +BB - Black backwards + +*/ \ No newline at end of file diff --git a/tests/mscript.js b/tests/mscript.js new file mode 100644 index 0000000..8a9f777 --- /dev/null +++ b/tests/mscript.js @@ -0,0 +1,169 @@ +'use strict' +const log = require('../lib/log')('mscript-tests') +const mscript = require('../lib/mscript') +//TODO: rewrite for mocha + +const tests = function tests () { + log.info('Running mscript tests') + console.time('Tests took') + + mscript.alts_unique(); //perform check only during tests + var fail = function (script, obj) { + log.error('...Failed :(') + log.error('script', script) + log.error('err', obj) + process.exit(1) + } + let script = +`CF +PF +CB +PB +BF +BB` + log.info('Basic function test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0 + && obj.arr.length === 6) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`CF +PF +CB +PB +BF +BB` + log.info('Functions with integers test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0 + && obj.arr.length === 6) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`CF 1000 +CB 1000 +SET PROJ 200 +PB 200` + log.info('Basic state test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`LOOP 10 +CF 3 +PF 1 +END LOOP` + log.info('Basic loop test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 30 + && obj.proj === 10 + && obj.arr.length === 40) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }); + + script = `LOOP 4\nLOOP 4\nPF\nBF\nEND LOOP\nEND LOOP` + log.info('Recursive loop test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 16 + && obj.proj === 16 + && obj.arr.length === 32) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + //Lighting tests + script = `L 255,255,255\nCF\nPF` + log.info('Basic light test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 1 + && obj.proj === 1 + && obj.arr.length === 2 + && obj.light.length === 2 + && obj.light[0] === '255,255,255' + && obj.light[1] === '') { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + script = 'L 255,255,255\nCF\nPF\nBF'; + log.info('Basic black test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 2 + && obj.proj === 1 + && obj.arr.length === 3 + && obj.light.length === 3 + && obj.light[0] === '255,255,255' + && obj.light[1] === '' + && obj.light[2] === mscript.black) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + script = 'LOOP 2\nL 1,1,1\nCF\nL 2,2,2\nCF\nEND'; + log.info('Basic light loop test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 4 + && obj.proj === 0 + && obj.arr.length === 4 + && obj.light.length === 4 + && obj.light[0] === '1,1,1' + && obj.light[3] === '2,2,2') { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + //LOOP W/ CAM and PROJ + script = 'LOOP 2\nCAM 4\nPROJ 4\nEND'; + log.info('Basic cam/proj loop test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 8 + && obj.proj === 8 + && obj.arr.length === 16 + && obj.light.length === 16 + && obj.light[0] === mscript.black) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + log.info('All tests completed'); + console.timeEnd('Tests took'); +} + +tests() \ No newline at end of file From 7f48045bbd6b8d62e1e1f011a75f857fe2fa0103 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:25:34 -0400 Subject: [PATCH 055/414] Log with appropriate tagging --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 77d6ca8..4356b13 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ 'use strict' const restify = require('restify') -const logger = require('winston') +const log = require('./lib/log')('main') const fs = require('fs') const ble = require('./lib/blootstrap') @@ -25,18 +25,19 @@ function createServer () { app.post('/sequence', () => {}) app.get('/status', rStatus) app.listen(PORT, () => { - console.log(`${APPNAME} listening on port ${PORT}!`) + log.info('server', { name : APPNAME, port : PORT }) }) } function rFrame (req, res, next) { + intval.frame() res.send({}) return next() } function rStatus (req, res, next) { const obj = intval.status() - res.send({}) + res.send(obj) return next() } From 82aa857ffdccab9ca270452989c58a4b5c678d4e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:25:54 -0400 Subject: [PATCH 056/414] Use log library instead of ipc log --- lib/blootstrap/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index 749c88a..f015ff7 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -1,6 +1,7 @@ 'use strict' const ipc = require('node-ipc') +const log = require('../log')('ble') function capitalize (s) { return s[0].toUpperCase() + s.slice(1) @@ -12,20 +13,20 @@ class Blootstrap { this._onData = () => {} ipc.connectTo('blootstrap_ble', () => { ipc.of.blootstrap_ble.on('connect', () => { - ipc.log(`Connected to the blootstrap_ble service`) + log.info('connect', `Connected to the blootstrap_ble service`) }) ipc.of.blootstrap_ble.on('data', data => { const str = data.toString() - ipc.log(str) + log.info('data', str) this._onData(str) }) ipc.of.blootstrap_ble.on('disconnect', () => { - ipc.log(`Disconnected from the blootstrap_ble service`) + log.info('disconnect', `Disconnected from the blootstrap_ble service`) }) ipc.of.blootstrap_ble.on('error', (err) => { if (err.code === 'EACCES') { - console.log(`Cannot access ipc`) + log.warn('error', `Cannot access ipc`) } }) }) From 495e59eae211b88187f4377b65fd9171fcf3ed0b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:26:13 -0400 Subject: [PATCH 057/414] Use log library in intval module --- lib/intval/index.js | 59 ++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 463c1bf..40d6124 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -6,7 +6,7 @@ let Gpio try { Gpio = require('onoff').Gpio } catch (e) { - console.warn('Failed including Gpio, using sim') + log.warn('Failed including Gpio, using sim') Gpio = require('../../lib/onoffsim').Gpio } @@ -55,11 +55,14 @@ class Intval { primed : false //is ready to stop frame } } + this._releaseMin = 50 this._releaseSequence = 1000 this._microDelay = 10 // delay after stop signal before stopping motors + this._declarePins() process.on('SIGINT', this._undeclarePins) + process.on('uncaughtException', this._undeclarePins) } /** * (internal function) Declares all Gpio pins that will be used @@ -71,8 +74,8 @@ class Intval { pin = PINS[p] if (pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir, pin.edge) if (!pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir) + log.info('_declarePins', { pin : pin.pin, dir : pin.dir, edge : pin.edge }) } - console.dir(this._pin) this._pin.release.watch(this._watchRelease) } /** @@ -81,10 +84,16 @@ class Intval { * */ _undeclarePins () { + if (!this._pin) return process.exit() + log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'}) + this._pin.fwd.writeSync(0) + log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'}) + this._pin.bwd.writeSync(0) this._pin.fwd.unexport() this._pin.bwd.unexport() this._pin.micro.unexport() this._pin.release.unexport() + process.exit() } /** * Start motor in forward direction by setting correct pins in h-bridge @@ -113,7 +122,7 @@ class Intval { let len = (+new Date()) - this._state.frame.start - console.log(`Frame stopped ${len}ms`) + log.info(`Frame stopped ${len}ms`) this._pin.micro.unwatch() this._state.frame.active = false @@ -138,21 +147,21 @@ class Intval { _watchMicro (err, val) { const NOW = +new Date() if (err) { - console.error(err) + log.error('_watchMicro', err) } //determine when to stop if (val === 0 && this._state.frame.active) { if (!this._state.micro.primed) { this._state.micro.primed = true this._state.micro.time = NOW - console.log('Mircoswitch primed to stop motor') + log.info('Mircoswitch primed to stop motor') } } else if (val === 1 && this._state.frame.active) { if (this._state.micro.primed) { this._state.micro.primed = false this._state.micro.time = 0 setTimeout( () => { - console.log(`Stopped frame after ${NOW - this._state.micro.time}ms`) + log.info(`Stopped frame after ${NOW - this._state.micro.time}ms`) }, this._microDelay) } } @@ -178,9 +187,9 @@ class Intval { const NOW = +new Date() let press = 0 if (err) { - return console.error(err) + return log.error(err) } - console.log(`Release switch val: ${val}`) + log.info(`Release switch val: ${val}`) if (val === 0) { //closed if ((!this._state.release.active && this._state.release.time === 0) || (this._state.release.active && (NOW - this._state.release.time) > (this._releaseSequence * 10)) @@ -197,7 +206,7 @@ class Intval { } else if (press >= this._releaseSequence) { this.sequence() } - console.log(`Release closed for ${press}ms`) + log.info(`Release closed for ${press}ms`) this._state.release.time = 0 this._state.release.active = false } @@ -213,7 +222,7 @@ class Intval { */ setDir (val = true) { if (typeof val !== 'boolean') { - return console.warn('Direction must be represented as either true or false') + return log.warn('Direction must be represented as either true or false') } this._state.dir = val } @@ -222,10 +231,9 @@ class Intval { * * @param {?boolean} [dir="null"] (optional) Direction of the frame * @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum - * @param {?integer} [delay="null"] (optional) Delay after frame before another can be started * */ - frame (dir = null, time = null, delay = null) { //may be overloaded, delay is suspect + frame (dir = null, time = null) { if (dir === true || (dir === null && this._state.dir === true) ) { dir = true } else { @@ -238,31 +246,16 @@ class Intval { time = 0 } - if (delay === null && this._state.delay !== 0) { - delay = this._state.delay - } else { - delay = 0 - } - this._state.frame.start = +new Date() this._state.frame.active = true this._pin.micro.watch(this._watchMicro) - if (delay !== 0) { - setTimeout(function () { - if (dir) { - this._startFwd() - } else { - this._startBwd() - } - - }, delay) + log.info('frame', {dir : dir, time : time}) + + if (dir) { + this._startFwd() } else { - if (dir) { - this._startFwd() - } else { - this._startBwd() - } + this._startBwd() } } /** @@ -270,7 +263,7 @@ class Intval { * */ sequence () { - console.log(`Started sequence`) + log.info('sequence', `Started sequence`) } status () { return this._state From ceb961ebd685ce8a637656556ec573740bf52f06 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:26:47 -0400 Subject: [PATCH 058/414] Add unexport function to onoffsim GPIO object --- lib/onoffsim/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/onoffsim/index.js b/lib/onoffsim/index.js index 7fac81f..88b8c70 100644 --- a/lib/onoffsim/index.js +++ b/lib/onoffsim/index.js @@ -25,6 +25,8 @@ const onoffsim = { }, watch : function (cb) { this.watchFunc = cb + }, + unexport : function () { } } } From 86ef664cd32328f862a646366fdc6038090e23fb Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:27:04 -0400 Subject: [PATCH 059/414] Update intval library documentation --- lib/intval/Readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 70b8b9f..967573a 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -14,7 +14,7 @@ Class representing the intval3 features * [._watchMicro(err, val)](#Intval+_watchMicro) * [._watchRelease(err, val)](#Intval+_watchRelease) * [.setDir([dir])](#Intval+setDir) - * [.frame([dir], [time], [delay])](#Intval+frame) + * [.frame([dir], [time])](#Intval+frame) * [.sequence()](#Intval+sequence) @@ -105,7 +105,7 @@ Set the default direction of the camera. -### intval.frame([dir], [time], [delay]) +### intval.frame([dir], [time]) Begin a single frame with set variables or defaults **Kind**: instance method of [Intval](#Intval) @@ -114,7 +114,6 @@ Begin a single frame with set variables or defaults | --- | --- | --- | --- | | [dir] | boolean | "null" | (optional) Direction of the frame | | [time] | integer | "null" | (optional) Exposure time, 0 = minimum | -| [delay] | integer | "null" | (optional) Delay after frame before another can be started | From b6a29f0e9e233d5cc31c0644e3ed8b5f870dfcce Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 17:57:28 -0400 Subject: [PATCH 060/414] Log all variables --- tests/gpio.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index dc61de8..7528b1a 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -33,7 +33,8 @@ function releaseTest () { if (err) { return console.error(err) } - console.log(`Release switch val: ${val}`) + //console.log(`Release switch val: ${val}`) + console.log(`RELEASE: ${val} ${active} ${NOW} ${saveTime}`) if (val === 0) { //console.log('closed') } else if (val === 1) { From 0e4cdb27782b449d8922f441962ba09cb492f68c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 19:04:14 -0400 Subject: [PATCH 061/414] Only test release for now --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 7528b1a..1bc340f 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -114,5 +114,5 @@ function stepupTest () { } releaseTest() -microTest() -stepupTest() +//microTest() +//stepupTest() From 988cf6a78633a1e043cbd59307f8f5c0f23c9e1f Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 19:12:15 -0400 Subject: [PATCH 062/414] Test microswitch now too --- tests/gpio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gpio.js b/tests/gpio.js index 1bc340f..f8992c5 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -114,5 +114,5 @@ function stepupTest () { } releaseTest() -//microTest() +microTest() //stepupTest() From 5097ed87ea1bf22e02aea1f6715f9ef7b0867b93 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 19:22:21 -0400 Subject: [PATCH 063/414] Only test microswitch --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index f8992c5..4971ff7 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -34,7 +34,7 @@ function releaseTest () { return console.error(err) } //console.log(`Release switch val: ${val}`) - console.log(`RELEASE: ${val} ${active} ${NOW} ${saveTime}`) + //console.log(`RELEASE: ${val} ${active} ${NOW} ${saveTime}`) if (val === 0) { //console.log('closed') } else if (val === 1) { @@ -57,7 +57,7 @@ function releaseTest () { } else if (NOW - saveTime >= 1000) { console.log('Started Sequence') } - console.log(`Release closed for ${NOW - saveTime}`) + //console.log(`Release closed for ${NOW - saveTime}`) saveTime = 0 active = false } From 56680db1beba5f00921b399929910e3d9ffe32a8 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 20:21:14 -0400 Subject: [PATCH 064/414] Run motor while watching microswitch --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 4971ff7..6713f06 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -113,6 +113,6 @@ function stepupTest () { bwd.writeSync(0) } -releaseTest() +//releaseTest() microTest() -//stepupTest() +stepupTest() From 1abc4ec7024e575576c6a29a59bd28624e38f609 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 20:26:37 -0400 Subject: [PATCH 065/414] run backwards --- tests/gpio.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 6713f06..10c0c43 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -108,9 +108,9 @@ function stepupTest () { fwd = Gpio(FWD, 'out') bwd = Gpio(BWD, 'out') - console.log(`Setting pin ${FWD} high`) - fwd.writeSync(1) - bwd.writeSync(0) + console.log(`Setting pin ${BWD} high`) + fwd.writeSync(0) + bwd.writeSync(1) } //releaseTest() From 9d665437bebd0522723e204782c62bd8b5c711ca Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:19:26 -0400 Subject: [PATCH 066/414] Motor works! forward and backwards! lib intval does not, however --- tests/gpio.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gpio.js b/tests/gpio.js index 10c0c43..b2ffbb3 100644 --- a/tests/gpio.js +++ b/tests/gpio.js @@ -113,6 +113,6 @@ function stepupTest () { bwd.writeSync(1) } -//releaseTest() +releaseTest() microTest() -stepupTest() +//stepupTest() From cb407b5057a04623572112bd04f9511c507459e4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:21:41 -0400 Subject: [PATCH 067/414] Switched pins for micro and release in the intval lib --- lib/intval/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 40d6124..451cd8c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -21,12 +21,12 @@ const PINS = { dir : 'out' }, micro : { - pin : 6, + pin : 5, dir : 'in', edge : 'both' }, release : { - pin : 5, + pin : 6, dir : 'in', edge : 'both' } From 2b06eb3b3c83cad9b0171016e77b9168c8c71dbc Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:24:20 -0400 Subject: [PATCH 068/414] Release pin is crashing lib --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index 451cd8c..16e3d4c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -211,6 +211,7 @@ class Intval { this._state.release.active = false } } + log.info('completed if statement') } /** * Set the default direction of the camera. From e27add8da718602e19d1d38423e37efa69f0568e Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:30:07 -0400 Subject: [PATCH 069/414] Refactor releaseWatch to use a seperate function to determine close state for the release switch --- lib/intval/index.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 16e3d4c..c9b309c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -172,7 +172,7 @@ class Intval { * * 1) If closed, start timer. * 2) If opened, check timer AND - * 3) If `press` (`NOW - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame + * 3) If `press` (`now - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame * 4) If `press` greater than `this._releaseSequence`, start sequence * * Button + 10K ohm resistor @@ -184,7 +184,7 @@ class Intval { * */ _watchRelease (err, val) { - const NOW = +new Date() + const now = +new Date() let press = 0 if (err) { return log.error(err) @@ -192,15 +192,14 @@ class Intval { log.info(`Release switch val: ${val}`) if (val === 0) { //closed - if ((!this._state.release.active && this._state.release.time === 0) || (this._state.release.active && (NOW - this._state.release.time) > (this._releaseSequence * 10)) - ) { - this._state.release.time = NOW + if (this._releaseClosedState(now)) { + this._state.release.time = now this._state.release.active = true //maybe unncecessary } } else if (val === 1) { //opened if (this._state.release.active) { - press = NOW - this._state.release.time + press = now - this._state.release.time if (press > this._releaseMin && press < this._releaseSequence) { this.frame() } else if (press >= this._releaseSequence) { @@ -213,6 +212,16 @@ class Intval { } log.info('completed if statement') } + + _releaseClosedState (now) { + if (!this._state.release.active && this._state.release.time === 0) { + return true + } + if (this._state.release.active && (now - this._state.release.time) > (this._releaseSequence * 10)) { + return true + } + return false + } /** * Set the default direction of the camera. * * forward = true From 53d50e715c9c170628f92d716a23ff5fabd5f3f0 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:34:14 -0400 Subject: [PATCH 070/414] No pins exit message --- lib/intval/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index c9b309c..0e51e30 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -84,7 +84,10 @@ class Intval { * */ _undeclarePins () { - if (!this._pin) return process.exit() + if (!this._pin) { + log.warn('_undeclarePins', { reason : 'No pins'}) + return process.exit() + } log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'}) this._pin.fwd.writeSync(0) log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'}) From 4818b44abfc72ad2227499ed937a3a43feb0c36b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:40:24 -0400 Subject: [PATCH 071/414] Test frame (and see why release switch is causing a hard exit) --- tests/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index e09e086..bae1cb4 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,3 +1,7 @@ 'use strict' -const intval = require('../lib/intval') \ No newline at end of file +const intval = require('../lib/intval') + +setTimeout(() => { + intval.frame() +}, 3000) \ No newline at end of file From 15d9225c04c0cb93d1bfd162a413a7bfa85d8054 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:42:52 -0400 Subject: [PATCH 072/414] Log error on uncaught exception --- lib/intval/index.js | 3 ++- tests/index.js | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 0e51e30..78d5b0b 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -83,7 +83,8 @@ class Intval { * that interupts the node process * */ - _undeclarePins () { + _undeclarePins (e) { + log.error(e) if (!this._pin) { log.warn('_undeclarePins', { reason : 'No pins'}) return process.exit() diff --git a/tests/index.js b/tests/index.js index bae1cb4..e09e086 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,7 +1,3 @@ 'use strict' -const intval = require('../lib/intval') - -setTimeout(() => { - intval.frame() -}, 3000) \ No newline at end of file +const intval = require('../lib/intval') \ No newline at end of file From 6218d8d585838be89d7ad67d4e13b5fb42015c08 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:52:13 -0400 Subject: [PATCH 073/414] Refactor intval library to use object. The nature of the GPIO module in use breaks references to this --- lib/intval/Readme.md | 74 +-- lib/intval/index.js | 478 +++++++++--------- package-lock.json | 1110 +----------------------------------------- tests/index.js | 4 +- 4 files changed, 280 insertions(+), 1386 deletions(-) diff --git a/lib/intval/Readme.md b/lib/intval/Readme.md index 967573a..fd7df24 100644 --- a/lib/intval/Readme.md +++ b/lib/intval/Readme.md @@ -1,54 +1,54 @@ - + -## Intval -Class representing the intval3 features +## intval +Object representing the intval3 features -**Kind**: global class +**Kind**: global constant -* [Intval](#Intval) - * [._declarePins()](#Intval+_declarePins) - * [._undeclarePins()](#Intval+_undeclarePins) - * [._startFwd()](#Intval+_startFwd) - * [._startBwd()](#Intval+_startBwd) - * [._stop()](#Intval+_stop) - * [._watchMicro(err, val)](#Intval+_watchMicro) - * [._watchRelease(err, val)](#Intval+_watchRelease) - * [.setDir([dir])](#Intval+setDir) - * [.frame([dir], [time])](#Intval+frame) - * [.sequence()](#Intval+sequence) +* [intval](#intval) + * [._declarePins()](#intval._declarePins) + * [._undeclarePins()](#intval._undeclarePins) + * [._startFwd()](#intval._startFwd) + * [._startBwd()](#intval._startBwd) + * [._stop()](#intval._stop) + * [._watchMicro(err, val)](#intval._watchMicro) + * [._watchRelease(err, val)](#intval._watchRelease) + * [.setDir([dir])](#intval.setDir) + * [.frame([dir], [time])](#intval.frame) + * [.sequence()](#intval.sequence) - + ### intval._declarePins() (internal function) Declares all Gpio pins that will be used -**Kind**: instance method of [Intval](#Intval) - +**Kind**: static method of [intval](#intval) + ### intval._undeclarePins() (internal function) Undeclares all Gpio in event of uncaught error that interupts the node process -**Kind**: instance method of [Intval](#Intval) - +**Kind**: static method of [intval](#intval) + ### intval._startFwd() Start motor in forward direction by setting correct pins in h-bridge -**Kind**: instance method of [Intval](#Intval) - +**Kind**: static method of [intval](#intval) + ### intval._startBwd() Start motor in backward direction by setting correct pins in h-bridge -**Kind**: instance method of [Intval](#Intval) - +**Kind**: static method of [intval](#intval) + ### intval._stop() Stop motor by setting both motor pins to 0 (LOW) -**Kind**: instance method of [Intval](#Intval) - +**Kind**: static method of [intval](#intval) + ### intval._watchMicro(err, val) Callback for watching relese switch state changes. @@ -61,14 +61,14 @@ Microswitch + 10K ohm resistor * 1 === open * 0 === closed -**Kind**: instance method of [Intval](#Intval) +**Kind**: static method of [intval](#intval) | Param | Type | Description | | --- | --- | --- | | err | object | Error object present if problem reading pin | | val | integer | Current value of the pin | - + ### intval._watchRelease(err, val) Callback for watching relese switch state changes. @@ -76,48 +76,48 @@ Using GPIO 05 on Raspberry Pi Zero W. 1) If closed, start timer. 2) If opened, check timer AND -3) If `press` (`NOW - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame -4) If `press` greater than `this._releaseSequence`, start sequence +3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._releaseSequence`, start frame +4) If `press` greater than `intval._releaseSequence`, start sequence Button + 10K ohm resistor * 1 === open * 0 === closed -**Kind**: instance method of [Intval](#Intval) +**Kind**: static method of [intval](#intval) | Param | Type | Description | | --- | --- | --- | | err | object | Error object present if problem reading pin | | val | integer | Current value of the pin | - + ### intval.setDir([dir]) Set the default direction of the camera. * forward = true * backward = false -**Kind**: instance method of [Intval](#Intval) +**Kind**: static method of [intval](#intval) | Param | Type | Default | Description | | --- | --- | --- | --- | | [dir] | boolean | true | Direction of the camera | - + ### intval.frame([dir], [time]) Begin a single frame with set variables or defaults -**Kind**: instance method of [Intval](#Intval) +**Kind**: static method of [intval](#intval) | Param | Type | Default | Description | | --- | --- | --- | --- | | [dir] | boolean | "null" | (optional) Direction of the frame | | [time] | integer | "null" | (optional) Exposure time, 0 = minimum | - + ### intval.sequence() Start a sequence of frames, using defaults or explicit instructions -**Kind**: instance method of [Intval](#Intval) +**Kind**: static method of [intval](#intval) diff --git a/lib/intval/index.js b/lib/intval/index.js index 78d5b0b..2a8aab2 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -32,256 +32,256 @@ const PINS = { } } -/** Class representing the intval3 features */ -class Intval { - constructor () { - this._pin = {} - this._state = { - dir : true, //forward - frame : { - start : 0, //time frame started, timestamp - active : false, //should frame be running - time : 0, //length of frame, in ms - delay : 0, //delay before start of frame, in ms +/** Object representing the intval3 features */ +const intval = {} - expected : 1000 //expected length of frame, in ms - }, - release : { - time: 0, - active : false //is pressed - }, - micro : { - time : 0, - primed : false //is ready to stop frame - } - } - - this._releaseMin = 50 - this._releaseSequence = 1000 - this._microDelay = 10 // delay after stop signal before stopping motors +intval.init = function () { + intval._pin = {} + intval._state = { + dir : true, //forward + frame : { + start : 0, //time frame started, timestamp + active : false, //should frame be running + time : 0, //length of frame, in ms + delay : 0, //delay before start of frame, in ms - this._declarePins() - process.on('SIGINT', this._undeclarePins) - process.on('uncaughtException', this._undeclarePins) - } - /** - * (internal function) Declares all Gpio pins that will be used - * - */ - _declarePins () { - let pin - for (let p in PINS) { - pin = PINS[p] - if (pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir, pin.edge) - if (!pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir) - log.info('_declarePins', { pin : pin.pin, dir : pin.dir, edge : pin.edge }) + expected : 1000 //expected length of frame, in ms + }, + release : { + time: 0, + active : false //is pressed + }, + micro : { + time : 0, + primed : false //is ready to stop frame } - this._pin.release.watch(this._watchRelease) } - /** - * (internal function) Undeclares all Gpio in event of uncaught error - * that interupts the node process - * - */ - _undeclarePins (e) { - log.error(e) - if (!this._pin) { - log.warn('_undeclarePins', { reason : 'No pins'}) - return process.exit() - } - log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'}) - this._pin.fwd.writeSync(0) - log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'}) - this._pin.bwd.writeSync(0) - this._pin.fwd.unexport() - this._pin.bwd.unexport() - this._pin.micro.unexport() - this._pin.release.unexport() - process.exit() - } - /** - * Start motor in forward direction by setting correct pins in h-bridge - * - */ - _startFwd () { - this._pin.fwd.writeSync(1) - this._pin.bwd.writeSync(0) - //start high-cpu watch - } - /** - * Start motor in backward direction by setting correct pins in h-bridge - * - */ - _startBwd () { - this._pin.fwd.writeSync(0) - this._pin.bwd.writeSync(1) - } - /** - * Stop motor by setting both motor pins to 0 (LOW) - * - */ - _stop () { - this._pin.fwd.writeSync(0) - this._pin.bwd.writeSync(0) + + intval._releaseMin = 50 + intval._releaseSequence = 1000 + intval._microDelay = 10 // delay after stop signal before stopping motors - let len = (+new Date()) - this._state.frame.start - - log.info(`Frame stopped ${len}ms`) - - this._pin.micro.unwatch() - this._state.frame.active = false - this._state.frame.start = 0 + intval._declarePins() + process.on('SIGINT', intval._undeclarePins) + process.on('uncaughtException', intval._undeclarePins) +} +/** +* (internal function) Declares all Gpio pins that will be used +* +*/ +intval._declarePins = function () { + let pin + for (let p in PINS) { + pin = PINS[p] + if (pin.edge) intval._pin[p] = Gpio(pin.pin, pin.dir, pin.edge) + if (!pin.edge) intval._pin[p] = Gpio(pin.pin, pin.dir) + log.info('_declarePins', { pin : pin.pin, dir : pin.dir, edge : pin.edge }) } - /** - * Callback for watching relese switch state changes. - * Using GPIO 06 on Raspberry Pi Zero W. - * - * 1) If closed AND frame active, start timer, set state primed to `true`. - * 1) If opened AND frame active, stop frame - * - * Microswitch + 10K ohm resistor - * * 1 === open - * * 0 === closed - * - * - * @param {object} err Error object present if problem reading pin - * @param {integer} val Current value of the pin - * - */ - _watchMicro (err, val) { - const NOW = +new Date() - if (err) { - log.error('_watchMicro', err) - } - //determine when to stop - if (val === 0 && this._state.frame.active) { - if (!this._state.micro.primed) { - this._state.micro.primed = true - this._state.micro.time = NOW - log.info('Mircoswitch primed to stop motor') - } - } else if (val === 1 && this._state.frame.active) { - if (this._state.micro.primed) { - this._state.micro.primed = false - this._state.micro.time = 0 - setTimeout( () => { - log.info(`Stopped frame after ${NOW - this._state.micro.time}ms`) - }, this._microDelay) - } - } - } - /** - * Callback for watching relese switch state changes. - * Using GPIO 05 on Raspberry Pi Zero W. - * - * 1) If closed, start timer. - * 2) If opened, check timer AND - * 3) If `press` (`now - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame - * 4) If `press` greater than `this._releaseSequence`, start sequence - * - * Button + 10K ohm resistor - * * 1 === open - * * 0 === closed - * - * @param {object} err Error object present if problem reading pin - * @param {integer} val Current value of the pin - * - */ - _watchRelease (err, val) { - const now = +new Date() - let press = 0 - if (err) { - return log.error(err) - } - log.info(`Release switch val: ${val}`) - if (val === 0) { - //closed - if (this._releaseClosedState(now)) { - this._state.release.time = now - this._state.release.active = true //maybe unncecessary - } - } else if (val === 1) { - //opened - if (this._state.release.active) { - press = now - this._state.release.time - if (press > this._releaseMin && press < this._releaseSequence) { - this.frame() - } else if (press >= this._releaseSequence) { - this.sequence() - } - log.info(`Release closed for ${press}ms`) - this._state.release.time = 0 - this._state.release.active = false - } - } - log.info('completed if statement') + intval._pin.release.watch(intval._watchRelease) +} +/** +* (internal function) Undeclares all Gpio in event of uncaught error +* that interupts the node process +* +*/ +intval._undeclarePins = function (e) { + log.error(e) + if (!intval._pin) { + log.warn('_undeclarePins', { reason : 'No pins'}) + return process.exit() } + log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'}) + intval._pin.fwd.writeSync(0) + log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'}) + intval._pin.bwd.writeSync(0) + intval._pin.fwd.unexport() + intval._pin.bwd.unexport() + intval._pin.micro.unexport() + intval._pin.release.unexport() + process.exit() +} +/** +* Start motor in forward direction by setting correct pins in h-bridge +* +*/ +intval._startFwd = function () { + intval._pin.fwd.writeSync(1) + intval._pin.bwd.writeSync(0) + //start high-cpu watch +} +/** +* Start motor in backward direction by setting correct pins in h-bridge +* +*/ +intval._startBwd = function () { + intval._pin.fwd.writeSync(0) + intval._pin.bwd.writeSync(1) +} +/** +* Stop motor by setting both motor pins to 0 (LOW) +* +*/ +intval._stop = function () { + intval._pin.fwd.writeSync(0) + intval._pin.bwd.writeSync(0) - _releaseClosedState (now) { - if (!this._state.release.active && this._state.release.time === 0) { - return true - } - if (this._state.release.active && (now - this._state.release.time) > (this._releaseSequence * 10)) { - return true - } - return false - } - /** - * Set the default direction of the camera. - * * forward = true - * * backward = false - * - * @param {boolean} [dir=true] Direction of the camera - * - */ - setDir (val = true) { - if (typeof val !== 'boolean') { - return log.warn('Direction must be represented as either true or false') - } - this._state.dir = val - } - /** - * Begin a single frame with set variables or defaults - * - * @param {?boolean} [dir="null"] (optional) Direction of the frame - * @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum - * - */ - frame (dir = null, time = null) { - if (dir === true || (dir === null && this._state.dir === true) ) { - dir = true - } else { - dir = false - } - - if (time === null && this._state.time !== 0) { - time = this._state.time - } else { - time = 0 - } + let len = (+new Date()) - intval._state.frame.start - this._state.frame.start = +new Date() - this._state.frame.active = true - this._pin.micro.watch(this._watchMicro) + log.info(`Frame stopped ${len}ms`) - log.info('frame', {dir : dir, time : time}) - - if (dir) { - this._startFwd() - } else { - this._startBwd() + intval._pin.micro.unwatch() + intval._state.frame.active = false + intval._state.frame.start = 0 +} +/** +* Callback for watching relese switch state changes. +* Using GPIO 06 on Raspberry Pi Zero W. +* +* 1) If closed AND frame active, start timer, set state primed to `true`. +* 1) If opened AND frame active, stop frame +* +* Microswitch + 10K ohm resistor +* * 1 === open +* * 0 === closed +* +* +* @param {object} err Error object present if problem reading pin +* @param {integer} val Current value of the pin +* +*/ +intval._watchMicro = function (err, val) { + const NOW = +new Date() + if (err) { + log.error('_watchMicro', err) + } + //determine when to stop + if (val === 0 && intval._state.frame.active) { + if (!intval._state.micro.primed) { + intval._state.micro.primed = true + intval._state.micro.time = NOW + log.info('Mircoswitch primed to stop motor') + } + } else if (val === 1 && intval._state.frame.active) { + if (intval._state.micro.primed) { + intval._state.micro.primed = false + intval._state.micro.time = 0 + setTimeout( () => { + log.info(`Stopped frame after ${NOW - intval._state.micro.time}ms`) + }, intval._microDelay) } - } - /** - * Start a sequence of frames, using defaults or explicit instructions - * - */ - sequence () { - log.info('sequence', `Started sequence`) - } - status () { - return this._state } } +/** +* Callback for watching relese switch state changes. +* Using GPIO 05 on Raspberry Pi Zero W. +* +* 1) If closed, start timer. +* 2) If opened, check timer AND +* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._releaseSequence`, start frame +* 4) If `press` greater than `intval._releaseSequence`, start sequence +* +* Button + 10K ohm resistor +* * 1 === open +* * 0 === closed +* +* @param {object} err Error object present if problem reading pin +* @param {integer} val Current value of the pin +* +*/ +intval._watchRelease = function (err, val) { + const now = +new Date() + let press = 0 + if (err) { + return log.error(err) + } + log.info(`Release switch val: ${val}`) + if (val === 0) { + //closed + if (intval._releaseClosedState(now)) { + intval._state.release.time = now + intval._state.release.active = true //maybe unncecessary + } + } else if (val === 1) { + //opened + if (intval._state.release.active) { + press = now - intval._state.release.time + if (press > intval._releaseMin && press < intval._releaseSequence) { + intval.frame() + } else if (press >= intval._releaseSequence) { + intval.sequence() + } + log.info(`Release closed for ${press}ms`) + intval._state.release.time = 0 + intval._state.release.active = false + } + } + log.info('completed if statement') +} -module.exports = new Intval() \ No newline at end of file +intval._releaseClosedState = function (now) { + if (!intval._state.release.active && intval._state.release.time === 0) { + return true + } + if (intval._state.release.active && (now - intval._state.release.time) > (intval._releaseSequence * 10)) { + return true + } + return false +} +/** +* Set the default direction of the camera. +* * forward = true +* * backward = false +* +* @param {boolean} [dir=true] Direction of the camera +* +*/ +intval.setDir = function (val = true) { + if (typeof val !== 'boolean') { + return log.warn('Direction must be represented as either true or false') + } + intval._state.dir = val +} +/** +* Begin a single frame with set variables or defaults +* +* @param {?boolean} [dir="null"] (optional) Direction of the frame +* @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum +* +*/ +intval.frame = function (dir = null, time = null) { + if (dir === true || (dir === null && intval._state.dir === true) ) { + dir = true + } else { + dir = false + } + + if (time === null && intval._state.time !== 0) { + time = intval._state.time + } else { + time = 0 + } + + intval._state.frame.start = +new Date() + intval._state.frame.active = true + intval._pin.micro.watch(intval._watchMicro) + + log.info('frame', {dir : dir, time : time}) + + if (dir) { + intval._startFwd() + } else { + intval._startBwd() + } +} +/** +* Start a sequence of frames, using defaults or explicit instructions +* +*/ +intval.sequence = function () { + log.info('sequence', `Started sequence`) +} +intval.status = function () { + return intval._state +} + +module.exports = intval \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bf33259..e8de43b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,8 +112,7 @@ "optional": true, "requires": { "debug": "2.6.8", - "nan": "2.6.2", - "usb": "1.2.0" + "nan": "2.6.2" } }, "bplist-parser": { @@ -1361,1113 +1360,6 @@ } } }, - "usb": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.2.0.tgz", - "integrity": "sha1-q3LZeGoEcGiRmgJNCMehxbHmD0c=", - "optional": true, - "requires": { - "nan": "2.6.2", - "node-pre-gyp": "0.6.30" - }, - "dependencies": { - "node-pre-gyp": { - "version": "0.6.30", - "bundled": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.0.0", - "rc": "1.1.6", - "request": "2.74.0", - "rimraf": "2.5.4", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.1.4" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "bundled": true - } - } - }, - "nopt": { - "version": "3.0.6", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1.0.9" - }, - "dependencies": { - "abbrev": { - "version": "1.0.9", - "bundled": true, - "optional": true - } - } - }, - "npmlog": { - "version": "4.0.0", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.2", - "console-control-strings": "1.1.0", - "gauge": "2.6.0", - "set-blocking": "2.0.0" - }, - "dependencies": { - "are-we-there-yet": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.1.5" - }, - "dependencies": { - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "readable-stream": { - "version": "2.1.5", - "bundled": true, - "optional": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "inherits": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "optional": true - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - } - } - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "gauge": { - "version": "2.6.0", - "bundled": true, - "optional": true, - "requires": { - "aproba": "1.0.4", - "console-control-strings": "1.1.0", - "has-color": "0.1.7", - "has-unicode": "2.0.1", - "object-assign": "4.1.0", - "signal-exit": "3.0.0", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.0" - }, - "dependencies": { - "aproba": { - "version": "1.0.4", - "bundled": true, - "optional": true - }, - "has-color": { - "version": "0.1.7", - "bundled": true, - "optional": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.0", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.0.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "code-point-at": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.0" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.0", - "bundled": true - } - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.0" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "bundled": true - } - } - }, - "wide-align": { - "version": "1.1.0", - "bundled": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - } - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - } - } - }, - "rc": { - "version": "1.1.6", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "0.4.1", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "1.0.4" - }, - "dependencies": { - "deep-extend": { - "version": "0.4.1", - "bundled": true, - "optional": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "optional": true - }, - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "strip-json-comments": { - "version": "1.0.4", - "bundled": true, - "optional": true - } - } - }, - "request": { - "version": "2.74.0", - "bundled": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.4.1", - "bl": "1.1.2", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.0", - "forever-agent": "0.6.1", - "form-data": "1.0.1", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.11", - "node-uuid": "1.4.7", - "oauth-sign": "0.8.2", - "qs": "6.2.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.1", - "tunnel-agent": "0.4.3" - }, - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "aws4": { - "version": "1.4.1", - "bundled": true, - "optional": true - }, - "bl": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "readable-stream": "2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "bundled": true, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "inherits": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "optional": true - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - } - } - } - } - }, - "caseless": { - "version": "0.11.0", - "bundled": true, - "optional": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - }, - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "bundled": true - } - } - }, - "extend": { - "version": "3.0.0", - "bundled": true, - "optional": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "optional": true - }, - "form-data": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "async": "2.0.1", - "combined-stream": "1.0.5", - "mime-types": "2.1.11" - }, - "dependencies": { - "async": { - "version": "2.0.1", - "bundled": true, - "optional": true, - "requires": { - "lodash": "4.15.0" - }, - "dependencies": { - "lodash": { - "version": "4.15.0", - "bundled": true, - "optional": true - } - } - } - } - }, - "har-validator": { - "version": "2.0.6", - "bundled": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.9.0", - "is-my-json-valid": "2.13.1", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "bundled": true, - "optional": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "bundled": true, - "optional": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "optional": true - }, - "has-ansi": { - "version": "2.0.0", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "bundled": true, - "optional": true - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "bundled": true, - "optional": true - } - } - }, - "supports-color": { - "version": "2.0.0", - "bundled": true, - "optional": true - } - } - }, - "commander": { - "version": "2.9.0", - "bundled": true, - "optional": true, - "requires": { - "graceful-readlink": "1.0.1" - }, - "dependencies": { - "graceful-readlink": { - "version": "1.0.1", - "bundled": true, - "optional": true - } - } - }, - "is-my-json-valid": { - "version": "2.13.1", - "bundled": true, - "optional": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "2.0.0", - "xtend": "4.0.1" - }, - "dependencies": { - "generate-function": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "generate-object-property": { - "version": "1.2.0", - "bundled": true, - "optional": true, - "requires": { - "is-property": "1.0.2" - }, - "dependencies": { - "is-property": { - "version": "1.0.2", - "bundled": true, - "optional": true - } - } - }, - "jsonpointer": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "xtend": { - "version": "4.0.1", - "bundled": true, - "optional": true - } - } - }, - "pinkie-promise": { - "version": "2.0.1", - "bundled": true, - "optional": true, - "requires": { - "pinkie": "2.0.4" - }, - "dependencies": { - "pinkie": { - "version": "2.0.4", - "bundled": true, - "optional": true - } - } - } - } - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - }, - "dependencies": { - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } - } - } - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.3.0", - "sshpk": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "jsprim": { - "version": "1.3.0", - "bundled": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2", - "json-schema": "0.2.2", - "verror": "1.3.6" - }, - "dependencies": { - "extsprintf": { - "version": "1.0.2", - "bundled": true - }, - "json-schema": { - "version": "0.2.2", - "bundled": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - } - } - }, - "sshpk": { - "version": "1.10.0", - "bundled": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.0", - "dashdash": "1.14.0", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.6", - "jodid25519": "1.0.2", - "jsbn": "0.1.0", - "tweetnacl": "0.13.3" - }, - "dependencies": { - "asn1": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "assert-plus": { - "version": "1.0.0", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.0", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.3" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.3", - "bundled": true, - "optional": true - } - } - }, - "dashdash": { - "version": "1.14.0", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.0" - } - }, - "getpass": { - "version": "0.1.6", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.0" - } - }, - "jsbn": { - "version": "0.1.0", - "bundled": true, - "optional": true - }, - "tweetnacl": { - "version": "0.13.3", - "bundled": true, - "optional": true - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "optional": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "optional": true - }, - "mime-types": { - "version": "2.1.11", - "bundled": true, - "requires": { - "mime-db": "1.23.0" - }, - "dependencies": { - "mime-db": { - "version": "1.23.0", - "bundled": true - } - } - }, - "node-uuid": { - "version": "1.4.7", - "bundled": true, - "optional": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "optional": true - }, - "qs": { - "version": "6.2.1", - "bundled": true, - "optional": true - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "optional": true - }, - "tough-cookie": { - "version": "2.3.1", - "bundled": true, - "optional": true - }, - "tunnel-agent": { - "version": "0.4.3", - "bundled": true, - "optional": true - } - } - }, - "rimraf": { - "version": "2.5.4", - "bundled": true, - "requires": { - "glob": "7.0.6" - }, - "dependencies": { - "glob": { - "version": "7.0.6", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.5", - "inherits": "2.0.1", - "minimatch": "3.0.3", - "once": "1.3.3", - "path-is-absolute": "1.0.0" - }, - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "inflight": { - "version": "1.0.5", - "bundled": true, - "requires": { - "once": "1.3.3", - "wrappy": "1.0.2" - }, - "dependencies": { - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "inherits": { - "version": "2.0.1", - "bundled": true - }, - "minimatch": { - "version": "3.0.3", - "bundled": true, - "requires": { - "brace-expansion": "1.1.6" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "bundled": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - } - } - } - } - }, - "once": { - "version": "1.3.3", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - }, - "dependencies": { - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "path-is-absolute": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.10", - "inherits": "2.0.1" - }, - "dependencies": { - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.1" - } - }, - "fstream": { - "version": "1.0.10", - "bundled": true, - "requires": { - "graceful-fs": "4.1.6", - "inherits": "2.0.1", - "mkdirp": "0.5.1", - "rimraf": "2.5.4" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.6", - "bundled": true - } - } - }, - "inherits": { - "version": "2.0.1", - "bundled": true - } - } - }, - "tar-pack": { - "version": "3.1.4", - "bundled": true, - "optional": true, - "requires": { - "debug": "2.2.0", - "fstream": "1.0.10", - "fstream-ignore": "1.0.5", - "once": "1.3.3", - "readable-stream": "2.1.5", - "rimraf": "2.5.4", - "tar": "2.2.1", - "uid-number": "0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.2.0", - "bundled": true, - "optional": true, - "requires": { - "ms": "0.7.1" - }, - "dependencies": { - "ms": { - "version": "0.7.1", - "bundled": true, - "optional": true - } - } - }, - "fstream": { - "version": "1.0.10", - "bundled": true, - "requires": { - "graceful-fs": "4.1.6", - "inherits": "2.0.1", - "mkdirp": "0.5.1", - "rimraf": "2.5.4" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.6", - "bundled": true - }, - "inherits": { - "version": "2.0.1", - "bundled": true - } - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "optional": true, - "requires": { - "fstream": "1.0.10", - "inherits": "2.0.1", - "minimatch": "3.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.3", - "bundled": true, - "optional": true, - "requires": { - "brace-expansion": "1.1.6" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "bundled": true, - "optional": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "optional": true - } - } - } - } - } - } - }, - "once": { - "version": "1.3.3", - "bundled": true, - "optional": true, - "requires": { - "wrappy": "1.0.2" - }, - "dependencies": { - "wrappy": { - "version": "1.0.2", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.1.5", - "bundled": true, - "optional": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "inherits": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "optional": true - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - } - } - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "optional": true - } - } - } - } - } - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/tests/index.js b/tests/index.js index e09e086..657e5f7 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,3 +1,5 @@ 'use strict' -const intval = require('../lib/intval') \ No newline at end of file +const intval = require('../lib/intval') + +intval.init() \ No newline at end of file From 0b47713c581acd7b6df1025f2f4a04eb92af07a9 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 21:59:38 -0400 Subject: [PATCH 074/414] Stop resetting frame.start to 0 --- lib/intval/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 2a8aab2..0e4d46d 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -131,7 +131,6 @@ intval._stop = function () { intval._pin.micro.unwatch() intval._state.frame.active = false - intval._state.frame.start = 0 } /** * Callback for watching relese switch state changes. @@ -215,7 +214,6 @@ intval._watchRelease = function (err, val) { intval._state.release.active = false } } - log.info('completed if statement') } intval._releaseClosedState = function (now) { From b7dd9e4fa19250bef6139083bf21182f189235ab Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 22:03:13 -0400 Subject: [PATCH 075/414] Was not actually calling intval._stop(). How embarassing? --- lib/intval/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 0e4d46d..63ed8d8 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -124,8 +124,8 @@ intval._startBwd = function () { intval._stop = function () { intval._pin.fwd.writeSync(0) intval._pin.bwd.writeSync(0) - - let len = (+new Date()) - intval._state.frame.start + const now = +new Date() + const len = now - intval._state.frame.start log.info(`Frame stopped ${len}ms`) @@ -158,14 +158,14 @@ intval._watchMicro = function (err, val) { if (!intval._state.micro.primed) { intval._state.micro.primed = true intval._state.micro.time = NOW - log.info('Mircoswitch primed to stop motor') + log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { if (intval._state.micro.primed) { intval._state.micro.primed = false intval._state.micro.time = 0 setTimeout( () => { - log.info(`Stopped frame after ${NOW - intval._state.micro.time}ms`) + intval._stop() }, intval._microDelay) } } From 2a08c648783be841e3901f13c96ab7b3094a6342 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 19 Oct 2017 22:13:34 -0400 Subject: [PATCH 076/414] Stop immediately --- lib/intval/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 63ed8d8..6b1015a 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -164,9 +164,9 @@ intval._watchMicro = function (err, val) { if (intval._state.micro.primed) { intval._state.micro.primed = false intval._state.micro.time = 0 - setTimeout( () => { + //setTimeout( () => { intval._stop() - }, intval._microDelay) + //}, intval._microDelay) } } } From 7d9377e7eabcfb57d28113a0e2b3991fb1f81310 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 20 Oct 2017 13:58:01 -0400 Subject: [PATCH 077/414] Start fleshing out API for web/mobile --- app/www/index.html | 2 +- index.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/www/index.html b/app/www/index.html index 7a642d7..faf95ee 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -4,7 +4,7 @@ intval 3 -

    + diff --git a/index.js b/index.js index 4356b13..b4d9728 100644 --- a/index.js +++ b/index.js @@ -19,6 +19,8 @@ let app = restify.createServer({ function createServer () { app.get('/', index) + app.get('/dir', rDir) + app.post('/dir', rDir) app.get('/frame', rFrame) app.post('/frame', rFrame) app.get('/sequence', () => {}) @@ -29,6 +31,10 @@ function createServer () { }) } +function rDir (req, res, next) { + +} + function rFrame (req, res, next) { intval.frame() res.send({}) From 429441137ee57266dae518bea9118ab497c198c2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 20 Oct 2017 13:59:19 -0400 Subject: [PATCH 078/414] Remove voltage step-up chips and add a voltage regulator for regulating 5v from a 12v source --- hardware/intval_3_prototype.fzz | Bin 9395 -> 5551 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/hardware/intval_3_prototype.fzz b/hardware/intval_3_prototype.fzz index 6eab7573975bae83fc4bd26b6e0171890d1d9a3a..2932926d676eb8ef5fd5f274e589dacd998cec18 100644 GIT binary patch literal 5551 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 From 7e989eeadfaaf010dfdd6f13f42a5d30dc4f799c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 19:36:23 -0400 Subject: [PATCH 079/414] Add beginnings of the arbitrary exposure time logic. Test with /tests/index.js file --- lib/intval/index.js | 29 ++++++++++++++++++++++++----- tests/index.js | 4 +++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 6b1015a..bc69ef3 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -40,12 +40,12 @@ intval.init = function () { intval._state = { dir : true, //forward frame : { - start : 0, //time frame started, timestamp + start : 0, //time frame started, timestamp active : false, //should frame be running - time : 0, //length of frame, in ms - delay : 0, //delay before start of frame, in ms - - expected : 1000 //expected length of frame, in ms + time : 0, //length of frame, in ms + delay : 0, //delay before start of frame, in ms + open : 300, //delay before pausing frame in open state + expected : 630 //expected length of frame, in ms }, release : { time: 0, @@ -117,6 +117,11 @@ intval._startBwd = function () { intval._pin.fwd.writeSync(0) intval._pin.bwd.writeSync(1) } + +intval._pause = function () { + intval._pin.fwd.writeSync(0) + intval._pin.fwd.writeSync(0) +} /** * Stop motor by setting both motor pins to 0 (LOW) * @@ -238,7 +243,13 @@ intval.setDir = function (val = true) { return log.warn('Direction must be represented as either true or false') } intval._state.dir = val +} +intval.setTime = function (val = 0) { + intval._state.frame.time = val } +intval.setDelay = function (val = 0) { + intval._state.frame.delay = val +} /** * Begin a single frame with set variables or defaults * @@ -270,6 +281,14 @@ intval.frame = function (dir = null, time = null) { } else { intval._startBwd() } + if (time !== null && time !== 0) { + setTimeout(intval._pause, intval._state.frame.open) + if (dir) { + setTimeout(intval._startFwd, 1000) + } else { + setTimeout(intval._startBwd, 1000) + } + } } /** * Start a sequence of frames, using defaults or explicit instructions diff --git a/tests/index.js b/tests/index.js index 657e5f7..47fbadb 100644 --- a/tests/index.js +++ b/tests/index.js @@ -2,4 +2,6 @@ const intval = require('../lib/intval') -intval.init() \ No newline at end of file +intval.init() +intval.setDir(false) +intval.setTime(1000) \ No newline at end of file From 16f8675339af33073c601bafbfef4a7535866ea0 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 19:38:26 -0400 Subject: [PATCH 080/414] OK backwards microswitch logic is not sound. Please revisit. --- tests/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index 47fbadb..f03fef0 100644 --- a/tests/index.js +++ b/tests/index.js @@ -3,5 +3,5 @@ const intval = require('../lib/intval') intval.init() -intval.setDir(false) + intval.setTime(1000) \ No newline at end of file From 43213cc6ceae7c9ba4ee0f5c7c53866c9f8d3f8c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 19:44:58 -0400 Subject: [PATCH 081/414] Match exposure time to intval2 logic. Try a 5 second exposure time. --- lib/intval/index.js | 7 +++++-- tests/index.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index bc69ef3..20e7459 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -243,12 +243,15 @@ intval.setDir = function (val = true) { return log.warn('Direction must be represented as either true or false') } intval._state.dir = val + log.info('setDir', { direction : dir ? 'forward' : 'backward' }) } intval.setTime = function (val = 0) { intval._state.frame.time = val + log.info('setTime', { time : val }) } intval.setDelay = function (val = 0) { intval._state.frame.delay = val + log.info('setDelay', { delay : val }) } /** * Begin a single frame with set variables or defaults @@ -284,9 +287,9 @@ intval.frame = function (dir = null, time = null) { if (time !== null && time !== 0) { setTimeout(intval._pause, intval._state.frame.open) if (dir) { - setTimeout(intval._startFwd, 1000) + setTimeout(intval._startFwd, (time - intval._state.frame.open)) } else { - setTimeout(intval._startBwd, 1000) + setTimeout(intval._startBwd, (time - intval._state.frame.open)) } } } diff --git a/tests/index.js b/tests/index.js index f03fef0..d956129 100644 --- a/tests/index.js +++ b/tests/index.js @@ -4,4 +4,4 @@ const intval = require('../lib/intval') intval.init() -intval.setTime(1000) \ No newline at end of file +intval.setTime(5000) \ No newline at end of file From d511ecdfbe2e56d88e6e43254f8899374a89670d Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 19:53:57 -0400 Subject: [PATCH 082/414] If statements were eating the frame continuance step. This has been altered by using 0 as the default value for arbitrary exposure frames. --- lib/intval/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 20e7459..862b535 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -269,8 +269,8 @@ intval.frame = function (dir = null, time = null) { if (time === null && intval._state.time !== 0) { time = intval._state.time - } else { - time = 0 + } else if (time === null) { + time = 0 //default speed } intval._state.frame.start = +new Date() @@ -284,7 +284,7 @@ intval.frame = function (dir = null, time = null) { } else { intval._startBwd() } - if (time !== null && time !== 0) { + if (time !== 0) { setTimeout(intval._pause, intval._state.frame.open) if (dir) { setTimeout(intval._startFwd, (time - intval._state.frame.open)) From 736a0c858bd2995715fa4286d2d4a9f93aac39ee Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 19:58:16 -0400 Subject: [PATCH 083/414] Log variables when pausing frames. --- lib/intval/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 862b535..f73d4a8 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -287,9 +287,13 @@ intval.frame = function (dir = null, time = null) { if (time !== 0) { setTimeout(intval._pause, intval._state.frame.open) if (dir) { - setTimeout(intval._startFwd, (time - intval._state.frame.open)) + log.info('frame', { pausing : time - intval._state.frame.open }) + setTimeout(() => { + log.info('frame', 'restarting after pause') + intval._startFwd() + }, time - intval._state.frame.open) } else { - setTimeout(intval._startBwd, (time - intval._state.frame.open)) + setTimeout(intval._startBwd, time - intval._state.frame.open) } } } From 6f73790f5aff990d628f0076ba11a65635bf1ffa Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:05:51 -0400 Subject: [PATCH 084/414] Was using undefined variable. Silly me. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index f73d4a8..389e849 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -267,7 +267,7 @@ intval.frame = function (dir = null, time = null) { dir = false } - if (time === null && intval._state.time !== 0) { + if (time === null && intval._state.frame.time !== 0) { time = intval._state.time } else if (time === null) { time = 0 //default speed From 4d16ab8921f830acf9da1fd8d0f9c1538a10d145 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:08:43 -0400 Subject: [PATCH 085/414] Confarnit! Caught the same error in another place, was referencing a variable to an undefined variable. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 389e849..da827aa 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -268,7 +268,7 @@ intval.frame = function (dir = null, time = null) { } if (time === null && intval._state.frame.time !== 0) { - time = intval._state.time + time = intval._state.frame.time } else if (time === null) { time = 0 //default speed } From ef77ecce83b3dd09a710ca73ca1b6cb0197e22ac Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:10:38 -0400 Subject: [PATCH 086/414] Log microswitch watch function because frames are being stopped prematurely --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index da827aa..4c4dd49 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -158,6 +158,7 @@ intval._watchMicro = function (err, val) { if (err) { log.error('_watchMicro', err) } + log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { if (!intval._state.micro.primed) { From 243f45c836c15fa0a68a98063badb040f390ee64 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:19:29 -0400 Subject: [PATCH 087/414] Harden the logic which allows exposures at arbitrary exposure times. A "pause" variable has been added to the frame state that tracks when the frame is intentionally paused, preventing the premature pausing of a single frame. --- lib/intval/index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 4c4dd49..e8a502d 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -42,6 +42,7 @@ intval.init = function () { frame : { start : 0, //time frame started, timestamp active : false, //should frame be running + paused : false, time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 300, //delay before pausing frame in open state @@ -121,6 +122,7 @@ intval._startBwd = function () { intval._pause = function () { intval._pin.fwd.writeSync(0) intval._pin.fwd.writeSync(0) + intval._state.micro.paused = true } /** * Stop motor by setting both motor pins to 0 (LOW) @@ -167,7 +169,7 @@ intval._watchMicro = function (err, val) { log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { - if (intval._state.micro.primed) { + if (intval._state.micro.primed && !intval._state.micro.paused) { intval._state.micro.primed = false intval._state.micro.time = 0 //setTimeout( () => { @@ -289,12 +291,15 @@ intval.frame = function (dir = null, time = null) { setTimeout(intval._pause, intval._state.frame.open) if (dir) { log.info('frame', { pausing : time - intval._state.frame.open }) - setTimeout(() => { - log.info('frame', 'restarting after pause') + setTimeout( () => { + intval._state.frame.paused = false intval._startFwd() }, time - intval._state.frame.open) } else { - setTimeout(intval._startBwd, time - intval._state.frame.open) + setTimeout( () => { + intval._state.frame.paused = false + intval._startBwd() + }, time - intval._state.frame.open) } } } From ca8f6b2be90da3e7213527336e50b63a4044dfd6 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:25:45 -0400 Subject: [PATCH 088/414] Logging when frames restart. I am still screwing up. --- lib/intval/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index e8a502d..8c77ce1 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -292,11 +292,13 @@ intval.frame = function (dir = null, time = null) { if (dir) { log.info('frame', { pausing : time - intval._state.frame.open }) setTimeout( () => { + log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() }, time - intval._state.frame.open) } else { setTimeout( () => { + log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() }, time - intval._state.frame.open) From 58267bf71c46860cc7042fa775387eb5c738f4a7 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:27:43 -0400 Subject: [PATCH 089/414] Set pause flag before frame is actually paused. To prevent stop events when motor slows. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 8c77ce1..a4a641e 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -122,7 +122,6 @@ intval._startBwd = function () { intval._pause = function () { intval._pin.fwd.writeSync(0) intval._pin.fwd.writeSync(0) - intval._state.micro.paused = true } /** * Stop motor by setting both motor pins to 0 (LOW) @@ -288,6 +287,7 @@ intval.frame = function (dir = null, time = null) { intval._startBwd() } if (time !== 0) { + intval._state.micro.paused = true setTimeout(intval._pause, intval._state.frame.open) if (dir) { log.info('frame', { pausing : time - intval._state.frame.open }) From 32e94fadb00f2b7969d42f37f54ab2d101f1a567 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:29:14 -0400 Subject: [PATCH 090/414] That was the wrong variable as well --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index a4a641e..da23048 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -287,7 +287,7 @@ intval.frame = function (dir = null, time = null) { intval._startBwd() } if (time !== 0) { - intval._state.micro.paused = true + intval._state.frame.paused = true setTimeout(intval._pause, intval._state.frame.open) if (dir) { log.info('frame', { pausing : time - intval._state.frame.open }) From c95af1693fe154d04316d7d675b788eeffba5002 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:31:28 -0400 Subject: [PATCH 091/414] Reduce open state variable by 50 ms, from 300 to 250 --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index da23048..26c82be 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -45,7 +45,7 @@ intval.init = function () { paused : false, time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms - open : 300, //delay before pausing frame in open state + open : 250, //delay before pausing frame in open state expected : 630 //expected length of frame, in ms }, release : { From 4908ccabcadf91656d8a32d7594b1735ffa2b58f Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:35:00 -0400 Subject: [PATCH 092/414] Use a padding value, instead of subtracting the open variable from the desired exposure. --- lib/intval/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 26c82be..94b2063 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -46,6 +46,7 @@ intval.init = function () { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 250, //delay before pausing frame in open state + pad : 450, expected : 630 //expected length of frame, in ms }, release : { @@ -295,13 +296,13 @@ intval.frame = function (dir = null, time = null) { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() - }, time - intval._state.frame.open) + }, time + intval._state.frame.pad) } else { setTimeout( () => { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() - }, time - intval._state.frame.open) + }, time + intval._state.frame.pad) } } } From 9ceb17c03bfa70090b4fbfcbfa592ea6afd81cbb Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:36:27 -0400 Subject: [PATCH 093/414] Forget pad, just use open --- lib/intval/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 94b2063..1c7ca25 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -46,7 +46,6 @@ intval.init = function () { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 250, //delay before pausing frame in open state - pad : 450, expected : 630 //expected length of frame, in ms }, release : { @@ -296,13 +295,13 @@ intval.frame = function (dir = null, time = null) { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() - }, time + intval._state.frame.pad) + }, time + intval._state.frame.open) } else { setTimeout( () => { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() - }, time + intval._state.frame.pad) + }, time + intval._state.frame.open) } } } From 6882c37cf0d6d8ab6a6aeb7184acce75e8ef909c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:39:07 -0400 Subject: [PATCH 094/414] Using a "closed" variable. That's the ticket. --- lib/intval/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 1c7ca25..46c940c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -46,6 +46,7 @@ intval.init = function () { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 250, //delay before pausing frame in open state + closed : 150, //time that frame actually remains closed for expected : 630 //expected length of frame, in ms }, release : { @@ -295,13 +296,13 @@ intval.frame = function (dir = null, time = null) { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() - }, time + intval._state.frame.open) + }, time + intval._state.frame.closed) } else { setTimeout( () => { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() - }, time + intval._state.frame.open) + }, time + intval._state.frame.closed) } } } From f74fa2798e60d7146442410a37f994b9db162e8e Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:42:11 -0400 Subject: [PATCH 095/414] Reduce "closed" time by 50ms. Is still too long when frame fires. Logging might be the culprit --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 46c940c..8dd12b4 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -46,7 +46,7 @@ intval.init = function () { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 250, //delay before pausing frame in open state - closed : 150, //time that frame actually remains closed for + closed : 100, //time that frame actually remains closed for expected : 630 //expected length of frame, in ms }, release : { From 4e1a3783f002799ecffb3db5913d9b6179567e88 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:43:54 -0400 Subject: [PATCH 096/414] Test backwards frames --- tests/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index d956129..d86c01b 100644 --- a/tests/index.js +++ b/tests/index.js @@ -4,4 +4,4 @@ const intval = require('../lib/intval') intval.init() -intval.setTime(5000) \ No newline at end of file +intval.setDir(false) \ No newline at end of file From 190732de1395f3234f9384207a3e2f247acd4c47 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:45:21 -0400 Subject: [PATCH 097/414] Whoops, referenced "dir" not "val" --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 8dd12b4..e0811b8 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -246,7 +246,7 @@ intval.setDir = function (val = true) { return log.warn('Direction must be represented as either true or false') } intval._state.dir = val - log.info('setDir', { direction : dir ? 'forward' : 'backward' }) + log.info('setDir', { direction : val ? 'forward' : 'backward' }) } intval.setTime = function (val = 0) { intval._state.frame.time = val From e09f1adbcd809a42de54123f6de59841a7028bb4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:54:35 -0400 Subject: [PATCH 098/414] Ok backwards frames are apparently doing fine. This commit uses a better organized object structure for release variables. --- lib/intval/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index e0811b8..9b0d7d2 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -58,9 +58,11 @@ intval.init = function () { primed : false //is ready to stop frame } } - - intval._releaseMin = 50 - intval._releaseSequence = 1000 + intval._frame = {} + intval._release = { + min : 20, + seq : 1000 + } intval._microDelay = 10 // delay after stop signal before stopping motors intval._declarePins() @@ -184,8 +186,8 @@ intval._watchMicro = function (err, val) { * * 1) If closed, start timer. * 2) If opened, check timer AND -* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._releaseSequence`, start frame -* 4) If `press` greater than `intval._releaseSequence`, start sequence +* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._release.seq`, start frame +* 4) If `press` greater than `intval._release.seq`, start sequence * * Button + 10K ohm resistor * * 1 === open @@ -212,9 +214,9 @@ intval._watchRelease = function (err, val) { //opened if (intval._state.release.active) { press = now - intval._state.release.time - if (press > intval._releaseMin && press < intval._releaseSequence) { + if (press > intval._release.min && press < intval._release.seq) { intval.frame() - } else if (press >= intval._releaseSequence) { + } else if (press >= intval._release.seq) { intval.sequence() } log.info(`Release closed for ${press}ms`) @@ -228,7 +230,7 @@ intval._releaseClosedState = function (now) { if (!intval._state.release.active && intval._state.release.time === 0) { return true } - if (intval._state.release.active && (now - intval._state.release.time) > (intval._releaseSequence * 10)) { + if (intval._state.release.active && (now - intval._state.release.time) > (intval._release.seq * 10)) { return true } return false From 3266a4ca14ca814f9576657696d6e49b8b50150b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:55:09 -0400 Subject: [PATCH 099/414] Test backwards timed frames. --- tests/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/index.js b/tests/index.js index d86c01b..f45b067 100644 --- a/tests/index.js +++ b/tests/index.js @@ -4,4 +4,5 @@ const intval = require('../lib/intval') intval.init() +intval.setTime(2000) intval.setDir(false) \ No newline at end of file From cc6311a01a0411121c84ca11517191accd799f8d Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 20:58:55 -0400 Subject: [PATCH 100/414] Add logging to backwards from forwards logic --- lib/intval/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 9b0d7d2..ab536fe 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -58,7 +58,9 @@ intval.init = function () { primed : false //is ready to stop frame } } - intval._frame = {} + intval._frame = { + + } intval._release = { min : 20, seq : 1000 @@ -293,13 +295,14 @@ intval.frame = function (dir = null, time = null) { intval._state.frame.paused = true setTimeout(intval._pause, intval._state.frame.open) if (dir) { - log.info('frame', { pausing : time - intval._state.frame.open }) + log.info('frame', { pausing : time + intval._state.frame.open }) setTimeout( () => { log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() }, time + intval._state.frame.closed) } else { + log.info('frame', { pausing : time + intval._state.frame.open }) setTimeout( () => { log.info('frame', 'restarting') intval._state.frame.paused = false From 5079adf04f69f3e91a2def496ad70fb1987556ef Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:01:58 -0400 Subject: [PATCH 101/414] Paused logging --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index ab536fe..cd861d6 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -127,6 +127,7 @@ intval._startBwd = function () { intval._pause = function () { intval._pin.fwd.writeSync(0) intval._pin.fwd.writeSync(0) + log.info('_pause', 'frame paused') } /** * Stop motor by setting both motor pins to 0 (LOW) From f4d7d3c5930c650005626ecfef9295fdf855d41c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:03:47 -0400 Subject: [PATCH 102/414] Wow. Just wow. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index cd861d6..898c686 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -126,7 +126,7 @@ intval._startBwd = function () { intval._pause = function () { intval._pin.fwd.writeSync(0) - intval._pin.fwd.writeSync(0) + intval._pin.bwd.writeSync(0) log.info('_pause', 'frame paused') } /** From b22712053cd89cda6ada8695f00e5ce1dc719e30 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:05:00 -0400 Subject: [PATCH 103/414] Remove excessive logging --- lib/intval/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 898c686..3a6f2aa 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -127,7 +127,7 @@ intval._startBwd = function () { intval._pause = function () { intval._pin.fwd.writeSync(0) intval._pin.bwd.writeSync(0) - log.info('_pause', 'frame paused') + //log.info('_pause', 'frame paused') } /** * Stop motor by setting both motor pins to 0 (LOW) @@ -206,7 +206,7 @@ intval._watchRelease = function (err, val) { if (err) { return log.error(err) } - log.info(`Release switch val: ${val}`) + //log.info(`Release switch val: ${val}`) if (val === 0) { //closed if (intval._releaseClosedState(now)) { @@ -222,7 +222,7 @@ intval._watchRelease = function (err, val) { } else if (press >= intval._release.seq) { intval.sequence() } - log.info(`Release closed for ${press}ms`) + //log.info(`Release closed for ${press}ms`) intval._state.release.time = 0 intval._state.release.active = false } @@ -296,16 +296,16 @@ intval.frame = function (dir = null, time = null) { intval._state.frame.paused = true setTimeout(intval._pause, intval._state.frame.open) if (dir) { - log.info('frame', { pausing : time + intval._state.frame.open }) + //log.info('frame', { pausing : time + intval._state.frame.open }) setTimeout( () => { - log.info('frame', 'restarting') + //log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() }, time + intval._state.frame.closed) } else { log.info('frame', { pausing : time + intval._state.frame.open }) setTimeout( () => { - log.info('frame', 'restarting') + //log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() }, time + intval._state.frame.closed) From 638530d2b2d41e8c8ae2bb47520d7b92b9bd0f23 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:07:43 -0400 Subject: [PATCH 104/414] Try a 400ms backwards open variable --- lib/intval/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 3a6f2aa..12b57f0 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -46,6 +46,7 @@ intval.init = function () { time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms open : 250, //delay before pausing frame in open state + openBwd : 400, closed : 100, //time that frame actually remains closed for expected : 630 //expected length of frame, in ms }, @@ -294,8 +295,9 @@ intval.frame = function (dir = null, time = null) { } if (time !== 0) { intval._state.frame.paused = true - setTimeout(intval._pause, intval._state.frame.open) + if (dir) { + setTimeout(intval._pause, intval._state.frame.open) //log.info('frame', { pausing : time + intval._state.frame.open }) setTimeout( () => { //log.info('frame', 'restarting') @@ -303,7 +305,7 @@ intval.frame = function (dir = null, time = null) { intval._startFwd() }, time + intval._state.frame.closed) } else { - log.info('frame', { pausing : time + intval._state.frame.open }) + setTimeout(intval._pause, intval._state.frame.openBwd) setTimeout( () => { //log.info('frame', 'restarting') intval._state.frame.paused = false From df106e6e90b8376d9129ede61195e355abb29a8a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:10:01 -0400 Subject: [PATCH 105/414] Remove some logging, clean up others --- lib/intval/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 12b57f0..8640229 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -140,7 +140,7 @@ intval._stop = function () { const now = +new Date() const len = now - intval._state.frame.start - log.info(`Frame stopped ${len}ms`) + log.info(`_stop`, { frame : len }) intval._pin.micro.unwatch() intval._state.frame.active = false @@ -166,13 +166,13 @@ intval._watchMicro = function (err, val) { if (err) { log.error('_watchMicro', err) } - log.info(`Microswitch val: ${val}`) + //log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { if (!intval._state.micro.primed) { intval._state.micro.primed = true intval._state.micro.time = NOW - log.info('Microswitch primed to stop motor') + //log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { if (intval._state.micro.primed && !intval._state.micro.paused) { From 58f8d09775f81e52439377f352ef2cf1e9bd5d78 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 20 Oct 2017 21:45:17 -0400 Subject: [PATCH 106/414] Try catch the connection --- lib/blootstrap/index.js | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index f015ff7..1e2c40f 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -11,25 +11,31 @@ function capitalize (s) { class Blootstrap { constructor () { this._onData = () => {} - ipc.connectTo('blootstrap_ble', () => { - ipc.of.blootstrap_ble.on('connect', () => { - log.info('connect', `Connected to the blootstrap_ble service`) + try { + ipc.connectTo('blootstrap_ble', () => { + ipc.of.blootstrap_ble.on('connect', () => { + log.info('connect', `Connected to the blootstrap_ble service`) + }) + ipc.of.blootstrap_ble.on('data', data => { + const str = data.toString() + log.info('data', str) + this._onData(str) + }) + ipc.of.blootstrap_ble.on('disconnect', () => { + log.info('disconnect', `Disconnected from the blootstrap_ble service`) + }) + ipc.of.blootstrap_ble.on('error', (err) => { + if (err.code === 'EACCES') { + log.warn('ble', `Cannot access ipc`) + } else { + log.error('ble', { error : err }) + } + }) }) - ipc.of.blootstrap_ble.on('data', data => { - const str = data.toString() - log.info('data', str) - this._onData(str) - }) - ipc.of.blootstrap_ble.on('disconnect', () => { - log.info('disconnect', `Disconnected from the blootstrap_ble service`) - }) - ipc.of.blootstrap_ble.on('error', (err) => { - if (err.code === 'EACCES') { - log.warn('error', `Cannot access ipc`) - } - }) - }) + } catch (e) { + log.error('connectTo', { error : e }) + } } /** * Binds functions to events that are triggered by BLE messages From 186b8324d197810d1edcabbb7da37f9dd0f71b35 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 11:46:06 -0400 Subject: [PATCH 107/414] Manage state from the webapp. Build all features there before mobile. --- app/www/index.html | 5 +++ index.js | 91 ++++++++++++++++++++++++++++++++++++++--- lib/intval/index.js | 56 ++++++++++++------------- nginx.conf | 28 ++----------- tests/curl_endpoints.sh | 3 ++ 5 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 tests/curl_endpoints.sh diff --git a/app/www/index.html b/app/www/index.html index faf95ee..78cf6f4 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -2,6 +2,11 @@ intval 3 +
    diff --git a/index.js b/index.js index b4d9728..e1233ac 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const restify = require('restify') const log = require('./lib/log')('main') const fs = require('fs') -const ble = require('./lib/blootstrap') +//const ble = require('./lib/blootstrap') const intval = require('./lib/intval') const PACKAGE = require('./package.json') @@ -18,9 +18,13 @@ let app = restify.createServer({ }) function createServer () { + app.use(restify.plugins.queryParser()) + app.use(restify.plugins.bodyParser({ mapParams: false })) app.get('/', index) app.get('/dir', rDir) app.post('/dir', rDir) + app.get('/exposure', rExposure) + app.post('/exposure', rExposure) app.get('/frame', rFrame) app.post('/frame', rFrame) app.get('/sequence', () => {}) @@ -32,7 +36,82 @@ function createServer () { } function rDir (req, res, next) { + let dir = true + let set = false + if (req.query && typeof req.query.dir !== 'undefined') { + if (typeof req.query.dir === 'string') { + dir = (req.query.dir === 'true') + } else { + dir = req.query.dir + } + set = true + } else if (req.body && typeof req.body.dir !== 'udnefined') { + if (typeof req.body.dir === 'string') { + dir = (req.body.dir === 'true') + } else { + dir = req.body.dir + } + set = true + } + if (set) { + intval.setDir(dir) + } else { + dir = intval._state.frame.dir + } + res.send({ dir : dir }) + return next() +} +function rExposure (req, res, next) { + let exposure = 0 + let set = false + if (req.query && typeof req.query.exposure !== 'undefined') { + if (typeof req.query.exposure === 'string') { + exposure = parseInt(req.query.exposure) + } else { + exposure = req.query.exposure + } + set = true + } else if (req.body && typeof req.body.exposure !== 'udnefined') { + if (typeof req.body.exposure === 'string') { + exposure = parseInt(req.body.exposure) + } else { + exposure = req.body.exposure + } + set = true + } + if (set) { + intval.setExposure(exposure) + } else { + exposure = intval._state.frame.exposure + } + res.send({ exposure : exposure }) +} + +function rDelay (req, res, next) { + let delay = 0 + let set = false + if (req.query && typeof req.query.delay !== 'undefined') { + if (typeof req.query.delay === 'string') { + delay = parseInt(req.query.delay) + } else { + delay = req.query.delay + } + set = true + } else if (req.body && typeof req.body.delay !== 'udnefined') { + if (typeof req.body.delay === 'string') { + delay = parseInt(req.body.delay) + } else { + delay = req.body.delay + } + set = true + } + if (set) { + intval.setDelay(delay) + } else { + delay = intval._state.frame.delay + } + res.send({ delay : delay }) } function rFrame (req, res, next) { @@ -48,22 +127,22 @@ function rStatus (req, res, next) { } function index (req, res, next) { - fs.readFile(INDEXPATH, (err, data) => { + fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { return next(err) } res.end(data) next() - }, 'utf8') + }) } - function init () { createServer() - ble.on('data', (str) => { + /*ble.on('data', (str) => { console.log(str) - }) + })*/ + //intval.init() } init() diff --git a/lib/intval/index.js b/lib/intval/index.js index 8640229..2ff04e7 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -38,17 +38,13 @@ const intval = {} intval.init = function () { intval._pin = {} intval._state = { - dir : true, //forward frame : { + dir : true, //forward start : 0, //time frame started, timestamp active : false, //should frame be running paused : false, - time : 0, //length of frame, in ms - delay : 0, //delay before start of frame, in ms - open : 250, //delay before pausing frame in open state - openBwd : 400, - closed : 100, //time that frame actually remains closed for - expected : 630 //expected length of frame, in ms + exposure : 0, //length of frame exposure, in ms + delay : 0 //delay before start of frame, in ms }, release : { time: 0, @@ -60,7 +56,10 @@ intval.init = function () { } } intval._frame = { - + open : 250, //delay before pausing frame in open state + openBwd : 400, + closed : 100, //time that frame actually remains closed for + expected : 630 //expected length of frame, in ms } intval._release = { min : 20, @@ -171,13 +170,13 @@ intval._watchMicro = function (err, val) { if (val === 0 && intval._state.frame.active) { if (!intval._state.micro.primed) { intval._state.micro.primed = true - intval._state.micro.time = NOW + intval._state.micro.exposure = NOW //log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { if (intval._state.micro.primed && !intval._state.micro.paused) { intval._state.micro.primed = false - intval._state.micro.time = 0 + intval._state.micro.exposure = 0 //setTimeout( () => { intval._stop() //}, intval._microDelay) @@ -190,7 +189,7 @@ intval._watchMicro = function (err, val) { * * 1) If closed, start timer. * 2) If opened, check timer AND -* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._release.seq`, start frame +* 3) If `press` (`now - intval._state.release.exposure`) greater than minimum and less than `intval._release.seq`, start frame * 4) If `press` greater than `intval._release.seq`, start sequence * * Button + 10K ohm resistor @@ -211,30 +210,30 @@ intval._watchRelease = function (err, val) { if (val === 0) { //closed if (intval._releaseClosedState(now)) { - intval._state.release.time = now + intval._state.release.exposure = now intval._state.release.active = true //maybe unncecessary } } else if (val === 1) { //opened if (intval._state.release.active) { - press = now - intval._state.release.time + press = now - intval._state.release.exposure if (press > intval._release.min && press < intval._release.seq) { intval.frame() } else if (press >= intval._release.seq) { intval.sequence() } //log.info(`Release closed for ${press}ms`) - intval._state.release.time = 0 + intval._state.release.exposure = 0 intval._state.release.active = false } } } intval._releaseClosedState = function (now) { - if (!intval._state.release.active && intval._state.release.time === 0) { + if (!intval._state.release.active && intval._state.release.exposure === 0) { return true } - if (intval._state.release.active && (now - intval._state.release.time) > (intval._release.seq * 10)) { + if (intval._state.release.active && (now - intval._state.release.exposure) > (intval._release.seq * 10)) { return true } return false @@ -251,13 +250,15 @@ intval.setDir = function (val = true) { if (typeof val !== 'boolean') { return log.warn('Direction must be represented as either true or false') } - intval._state.dir = val + intval._state.frame.dir = val log.info('setDir', { direction : val ? 'forward' : 'backward' }) } -intval.setTime = function (val = 0) { - intval._state.frame.time = val + +intval.setExposure = function (val = 0) { + intval._state.frame.exposure = val log.info('setTime', { time : val }) } + intval.setDelay = function (val = 0) { intval._state.frame.delay = val log.info('setDelay', { delay : val }) @@ -270,14 +271,14 @@ intval.setDelay = function (val = 0) { * */ intval.frame = function (dir = null, time = null) { - if (dir === true || (dir === null && intval._state.dir === true) ) { + if (dir === true || (dir === null && intval._state.frame.dir === true) ) { dir = true } else { dir = false } - if (time === null && intval._state.frame.time !== 0) { - time = intval._state.frame.time + if (time === null && intval._state.frame.exposure !== 0) { + time = intval._state.frame.exposure } else if (time === null) { time = 0 //default speed } @@ -297,20 +298,19 @@ intval.frame = function (dir = null, time = null) { intval._state.frame.paused = true if (dir) { - setTimeout(intval._pause, intval._state.frame.open) - //log.info('frame', { pausing : time + intval._state.frame.open }) + setTimeout(intval._pause, intval._frame.open) + //log.info('frame', { pausing : time + intval._frame.open }) setTimeout( () => { - //log.info('frame', 'restarting') intval._state.frame.paused = false intval._startFwd() - }, time + intval._state.frame.closed) + }, time + intval._frame.closed) } else { - setTimeout(intval._pause, intval._state.frame.openBwd) + setTimeout(intval._pause, intval._frame.openBwd) setTimeout( () => { //log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() - }, time + intval._state.frame.closed) + }, time + intval._frame.closed) } } } diff --git a/nginx.conf b/nginx.conf index cc71e29..7b41907 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,28 +1,8 @@ #blootstrap nginx conf -#uncomment and modify following files for ssl -#server { - - #listen 80; - #server_name my_project; - #return 301 https://$server_name$request_uri; - -#} - server { listen 80; - #listen 443 ssl; - #ssl on; - #ssl_certificate {{SSL_CERT_PATH}}; - #ssl_certificate_key {{SSL_KEY_PATH}}; - - #ssl_session_timeout 5m; - #ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; - #ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; - #ssl_prefer_server_ciphers on; - - #server_name my_project; location / { proxy_pass http://127.0.0.1:6699/; @@ -30,8 +10,8 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; gzip on; - gzip_comp_level 5; - gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; + gzip_comp_level 5; + gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; } #uncomment for static file server #location /static/ { @@ -40,8 +20,8 @@ server { #access_log off; #add_header Cache-Control "public"; #gzip on; - #gzip_comp_level 5; - #gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; + #gzip_comp_level 5; + #gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; #use project location #alias /var/node/intval3/static/; #} diff --git a/tests/curl_endpoints.sh b/tests/curl_endpoints.sh new file mode 100644 index 0000000..7ee31b2 --- /dev/null +++ b/tests/curl_endpoints.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +curl -H "Content-Type: application/json" -X POST -d '{"dir" : false }' http://localhost:6699/dir \ No newline at end of file From 06bdb8b76348d107339da030eba6762f7d545a84 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 11:47:55 -0400 Subject: [PATCH 108/414] Need to initialize intval in server code. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index e1233ac..be29495 100644 --- a/index.js +++ b/index.js @@ -142,7 +142,7 @@ function init () { /*ble.on('data', (str) => { console.log(str) })*/ - //intval.init() + intval.init() } init() From 19e65115594b71b580ebaea39310be366d212d0b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 11:49:29 -0400 Subject: [PATCH 109/414] Update tests to use latest api --- tests/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index f45b067..7f8e889 100644 --- a/tests/index.js +++ b/tests/index.js @@ -4,5 +4,5 @@ const intval = require('../lib/intval') intval.init() -intval.setTime(2000) +intval.setExposure(2000) intval.setDir(false) \ No newline at end of file From e2ee307824480519cce4aa6c463a8bb5f3767d15 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 11:52:41 -0400 Subject: [PATCH 110/414] Add logging back because release no longer works --- lib/intval/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 2ff04e7..95d03a7 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -113,7 +113,6 @@ intval._undeclarePins = function (e) { intval._startFwd = function () { intval._pin.fwd.writeSync(1) intval._pin.bwd.writeSync(0) - //start high-cpu watch } /** * Start motor in backward direction by setting correct pins in h-bridge @@ -206,7 +205,7 @@ intval._watchRelease = function (err, val) { if (err) { return log.error(err) } - //log.info(`Release switch val: ${val}`) + log.info(`Release switch val: ${val}`) if (val === 0) { //closed if (intval._releaseClosedState(now)) { From 41360405c3293c39d2b471be990800aecf26077f Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 11:54:45 -0400 Subject: [PATCH 111/414] Lol. Was a little overzealous with a replace all. _state.release.time should never have been changed to exposure. --- lib/intval/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 95d03a7..1974015 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -188,7 +188,7 @@ intval._watchMicro = function (err, val) { * * 1) If closed, start timer. * 2) If opened, check timer AND -* 3) If `press` (`now - intval._state.release.exposure`) greater than minimum and less than `intval._release.seq`, start frame +* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._release.seq`, start frame * 4) If `press` greater than `intval._release.seq`, start sequence * * Button + 10K ohm resistor @@ -205,34 +205,34 @@ intval._watchRelease = function (err, val) { if (err) { return log.error(err) } - log.info(`Release switch val: ${val}`) + //log.info(`Release switch val: ${val}`) if (val === 0) { //closed if (intval._releaseClosedState(now)) { - intval._state.release.exposure = now + intval._state.release.time = now intval._state.release.active = true //maybe unncecessary } } else if (val === 1) { //opened if (intval._state.release.active) { - press = now - intval._state.release.exposure + press = now - intval._state.release.time if (press > intval._release.min && press < intval._release.seq) { intval.frame() } else if (press >= intval._release.seq) { intval.sequence() } //log.info(`Release closed for ${press}ms`) - intval._state.release.exposure = 0 + intval._state.release.time = 0 intval._state.release.active = false } } } intval._releaseClosedState = function (now) { - if (!intval._state.release.active && intval._state.release.exposure === 0) { + if (!intval._state.release.active && intval._state.release.time === 0) { return true } - if (intval._state.release.active && (now - intval._state.release.exposure) > (intval._release.seq * 10)) { + if (intval._state.release.active && (now - intval._state.release.time) > (intval._release.seq * 10)) { return true } return false From 0848d5bfc0d766d57b51ee3de537b943eb8c1c19 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 12:08:12 -0400 Subject: [PATCH 112/414] Add logging to webapp and callback to frame function in intval --- index.js | 21 ++++++++++++++------- lib/intval/index.js | 9 ++++++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index be29495..6c1f975 100644 --- a/index.js +++ b/index.js @@ -20,16 +20,16 @@ let app = restify.createServer({ function createServer () { app.use(restify.plugins.queryParser()) app.use(restify.plugins.bodyParser({ mapParams: false })) - app.get('/', index) - app.get('/dir', rDir) + app.get( '/', index) + app.get( '/dir', rDir) app.post('/dir', rDir) - app.get('/exposure', rExposure) + app.get( '/exposure', rExposure) app.post('/exposure', rExposure) - app.get('/frame', rFrame) + app.get( '/frame', rFrame) app.post('/frame', rFrame) - app.get('/sequence', () => {}) + app.get( '/sequence', () => {}) app.post('/sequence', () => {}) - app.get('/status', rStatus) + app.get( '/status', rStatus) app.listen(PORT, () => { log.info('server', { name : APPNAME, port : PORT }) }) @@ -58,6 +58,7 @@ function rDir (req, res, next) { } else { dir = intval._state.frame.dir } + log.info('/dir', { method: req.method, set : set, dir : dir}) res.send({ dir : dir }) return next() } @@ -85,7 +86,9 @@ function rExposure (req, res, next) { } else { exposure = intval._state.frame.exposure } + log.info('/exposure', { method: req.method, set : set, exposure : exposure }) res.send({ exposure : exposure }) + return next() } function rDelay (req, res, next) { @@ -111,11 +114,15 @@ function rDelay (req, res, next) { } else { delay = intval._state.frame.delay } + log.info('/delay', { method: req.method, set : set, delay : delay }) res.send({ delay : delay }) + return next() } function rFrame (req, res, next) { - intval.frame() + intval.frame(cb=()=>{ + log.info('happened') + }) res.send({}) return next() } diff --git a/lib/intval/index.js b/lib/intval/index.js index 1974015..20b9dae 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -44,7 +44,8 @@ intval.init = function () { active : false, //should frame be running paused : false, exposure : 0, //length of frame exposure, in ms - delay : 0 //delay before start of frame, in ms + delay : 0, //delay before start of frame, in ms + cb : () => {} }, release : { time: 0, @@ -142,6 +143,7 @@ intval._stop = function () { intval._pin.micro.unwatch() intval._state.frame.active = false + if (intval._state.frame.cb) intval._state.frame.cb() } /** * Callback for watching relese switch state changes. @@ -269,7 +271,7 @@ intval.setDelay = function (val = 0) { * @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum * */ -intval.frame = function (dir = null, time = null) { +intval.frame = function (dir = null, time = null, cb = () => {}) { if (dir === true || (dir === null && intval._state.frame.dir === true) ) { dir = true } else { @@ -286,7 +288,7 @@ intval.frame = function (dir = null, time = null) { intval._state.frame.active = true intval._pin.micro.watch(intval._watchMicro) - log.info('frame', {dir : dir, time : time}) + log.info('frame', {dir : dir ? 'forward' : 'backward', time : time}) if (dir) { intval._startFwd() @@ -312,6 +314,7 @@ intval.frame = function (dir = null, time = null) { }, time + intval._frame.closed) } } + intval._state.frame.cb = cb } /** * Start a sequence of frames, using defaults or explicit instructions From 9302cf1b21b9cc340512d9b8c35cedf44f039e03 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 12:10:16 -0400 Subject: [PATCH 113/414] Force example frame before evaluating query and body of post for parameters --- index.js | 5 +++-- lib/blootstrap/index.js | 43 +++++++++++++++++++---------------------- lib/wifi/index.js | 30 ++++++++++++++-------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/index.js b/index.js index 6c1f975..a92753e 100644 --- a/index.js +++ b/index.js @@ -120,8 +120,9 @@ function rDelay (req, res, next) { } function rFrame (req, res, next) { - intval.frame(cb=()=>{ - log.info('happened') + log.info('/frame', { method : req.method }) + intval.frame(true, 0, () => { + log.info('frame stopped') }) res.send({}) return next() diff --git a/lib/blootstrap/index.js b/lib/blootstrap/index.js index 1e2c40f..b4003a4 100644 --- a/lib/blootstrap/index.js +++ b/lib/blootstrap/index.js @@ -11,31 +11,28 @@ function capitalize (s) { class Blootstrap { constructor () { this._onData = () => {} - try { - ipc.connectTo('blootstrap_ble', () => { - ipc.of.blootstrap_ble.on('connect', () => { - log.info('connect', `Connected to the blootstrap_ble service`) + ipc.config.maxRetries = 0 + ipc.connectTo('blootstrap_ble', () => { + ipc.of.blootstrap_ble.on('connect', () => { + log.info('connect', `Connected to the blootstrap_ble service`) - }) - ipc.of.blootstrap_ble.on('data', data => { - const str = data.toString() - log.info('data', str) - this._onData(str) - }) - ipc.of.blootstrap_ble.on('disconnect', () => { - log.info('disconnect', `Disconnected from the blootstrap_ble service`) - }) - ipc.of.blootstrap_ble.on('error', (err) => { - if (err.code === 'EACCES') { - log.warn('ble', `Cannot access ipc`) - } else { - log.error('ble', { error : err }) - } - }) }) - } catch (e) { - log.error('connectTo', { error : e }) - } + ipc.of.blootstrap_ble.on('data', data => { + const str = data.toString() + log.info('data', str) + this._onData(str) + }) + ipc.of.blootstrap_ble.on('disconnect', () => { + log.info('disconnect', `Disconnected from the blootstrap_ble service`) + }) + ipc.of.blootstrap_ble.on('error', (err) => { + if (err.code === 'EACCES') { + log.warn('ble', `Cannot access ipc`) + } else { + log.error('ble', { error : err }) + } + }) + }) } /** * Binds functions to events that are triggered by BLE messages diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 2f236d2..b07d74a 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -13,12 +13,14 @@ const log = require('../log')('wifi') const exec = require('child_process').exec const fs = require('fs') +let _entry = null +let _ssid = null +let _cb = null + /** Class representing the wifi features */ class Wifi { constructor () { - this._callback = () => {} - this._entry = null - this._ssid = null + } /** * List available wifi access points @@ -54,14 +56,14 @@ class Wifi { _readConfigCb (err, data) { if (err) { console.error(err) - return this._callback(err) + return _cb(err) } if (data.search(networkPattern) === -1) { - data += `\n${this._entry}` + data += `\n${_entry}` } else { - data = data.replace(networkPattern, this._entry) + data = data.replace(networkPattern, _entry) } - this._entry = null + _entry = null fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** @@ -73,7 +75,7 @@ class Wifi { _writeConfigCb (err) { if (err) { console.error(err) - return this._callback(err) + return _cb(err) } exec(reconfigure, this._reconfigureCb) } @@ -87,7 +89,7 @@ class Wifi { _reconfigureCb (err, stdout, stderr) { if (err) { console.error(err) - return this._callback(err) + return _cb(err) } console.log('Wifi reconfigured') exec(refresh, this._refreshCb) @@ -102,11 +104,11 @@ class Wifi { _refreshCb (err, stdout, stderr) { if (err) { console.error(err) - return this._callback(err) + return _cb(err) } console.log('Wifi refreshed') //this._callback(null, { ssid : ssid, pwd : pwd.length }) - this._callback = () => {} + _cb = () => {} } /** * Function which initializes the processes for adding a wifi access point authentication @@ -116,9 +118,9 @@ class Wifi { * @param {function} callback Function invoked after process is complete, or fails */ setNetwork (ssid, pwd, callback) { - this._entry = `network={\n\tssid="${ssid}"\n\tpsk="${pwd}"\n}\n` - this._callback = callback - this._ssid = ssid + _entry = `network={\n\tssid="${ssid}"\n\tpsk="${pwd}"\n}\n` + _cb = callback + _ssid = ssid fs.readFile(filePath, 'utf8', this._readConfigCb) } /** From 0b245a00c42e749707cad306ac112fdb97e4a6a3 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 12:21:15 -0400 Subject: [PATCH 114/414] Log after frame is complete --- index.js | 7 +++---- lib/intval/index.js | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index a92753e..5314588 100644 --- a/index.js +++ b/index.js @@ -121,11 +121,10 @@ function rDelay (req, res, next) { function rFrame (req, res, next) { log.info('/frame', { method : req.method }) - intval.frame(true, 0, () => { - log.info('frame stopped') + intval.frame(true, 0, (dir, len) => { + res.send({ dir : true, len : len}) + return next() }) - res.send({}) - return next() } function rStatus (req, res, next) { diff --git a/lib/intval/index.js b/lib/intval/index.js index 20b9dae..81f56ad 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -297,7 +297,6 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { } if (time !== 0) { intval._state.frame.paused = true - if (dir) { setTimeout(intval._pause, intval._frame.open) //log.info('frame', { pausing : time + intval._frame.open }) @@ -314,7 +313,9 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { }, time + intval._frame.closed) } } - intval._state.frame.cb = cb + intval._state.frame.cb = (len) => { + cb(dir, len) + } } /** * Start a sequence of frames, using defaults or explicit instructions From c2f372c7feab1c3d8c590685be84f9d929dfab45 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 12:23:37 -0400 Subject: [PATCH 115/414] Actually pass the required information into the callback function --- lib/intval/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 81f56ad..06219b1 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -143,7 +143,7 @@ intval._stop = function () { intval._pin.micro.unwatch() intval._state.frame.active = false - if (intval._state.frame.cb) intval._state.frame.cb() + if (intval._state.frame.cb) intval._state.frame.cb(len) } /** * Callback for watching relese switch state changes. @@ -257,7 +257,7 @@ intval.setDir = function (val = true) { intval.setExposure = function (val = 0) { intval._state.frame.exposure = val - log.info('setTime', { time : val }) + log.info('setTime', { exposure : val }) } intval.setDelay = function (val = 0) { @@ -288,7 +288,7 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { intval._state.frame.active = true intval._pin.micro.watch(intval._watchMicro) - log.info('frame', {dir : dir ? 'forward' : 'backward', time : time}) + log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : time}) if (dir) { intval._startFwd() From da3908c2e57c182eeab33724e905b42d575239b4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 19:31:52 -0400 Subject: [PATCH 116/414] Add logic to prevent frames shorter than 250ms. Might work for now. Seemingly only happening with web requests? --- lib/intval/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 06219b1..fa27623 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -162,7 +162,7 @@ intval._stop = function () { * */ intval._watchMicro = function (err, val) { - const NOW = +new Date() + const now = +new Date() if (err) { log.error('_watchMicro', err) } @@ -171,13 +171,13 @@ intval._watchMicro = function (err, val) { if (val === 0 && intval._state.frame.active) { if (!intval._state.micro.primed) { intval._state.micro.primed = true - intval._state.micro.exposure = NOW + intval._state.micro.time = now //log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { - if (intval._state.micro.primed && !intval._state.micro.paused) { + if (intval._state.micro.primed && !intval._state.micro.paused && (now - intval._state.frame.start) > intval._frame.open) { intval._state.micro.primed = false - intval._state.micro.exposure = 0 + intval._state.micro.time = 0 //setTimeout( () => { intval._stop() //}, intval._microDelay) From d7ca2ebe46242eea915a525d20414448e5b94861 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 20:12:39 -0400 Subject: [PATCH 117/414] All features added to /frame endpoint, can set direction independent of the state and can set the exposure as well. --- index.js | 42 +++++++++++++++++++++++++++++++++++++++--- lib/intval/index.js | 2 +- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 5314588..019cb99 100644 --- a/index.js +++ b/index.js @@ -120,9 +120,45 @@ function rDelay (req, res, next) { } function rFrame (req, res, next) { - log.info('/frame', { method : req.method }) - intval.frame(true, 0, (dir, len) => { - res.send({ dir : true, len : len}) + let dir = true + let exposure = 0 + if (intval._state.frame.dir !== true) { + dir = false + } + if (intval._state.frame.exposure !== 0) { + exposure = intval._state.frame.exposure + } + if (req.query && typeof req.query.dir !== 'undefined') { + if (typeof req.query.dir === 'string') { + dir = (req.query.dir === 'true') + } else { + dir = req.query.dir + } + } + if (req.body && typeof req.body.dir !== 'undefined') { + if (typeof req.body.dir === 'string') { + dir = (req.body.dir === 'true') + } else { + dir = req.body.dir + } + } + if (req.query && typeof req.query.exposure !== 'undefined') { + if (typeof req.query.exposure === 'string') { + exposure = parseInt(req.query.exposure) + } else { + exposure = req.query.exposure + } + } + if (req.body && typeof req.body.exposure !== 'undefined') { + if (typeof req.body.exposure === 'string') { + exposure = parseInt(req.body.exposure) + } else { + exposure = req.body.exposure + } + } + log.info('/frame', { method : req.method, dir : dir, exposure : exposure }) + intval.frame(dir, exposure, (len) => { + res.send({ dir : dir, len : len}) return next() }) } diff --git a/lib/intval/index.js b/lib/intval/index.js index fa27623..3b9d54c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -314,7 +314,7 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { } } intval._state.frame.cb = (len) => { - cb(dir, len) + cb(len) } } /** From 83fea51433d347e3517bc8c19ce695cd46cf98b8 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 21:07:45 -0400 Subject: [PATCH 118/414] Start UI, add jquery and a switch for web and mobile methods --- app/www/index.html | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 78cf6f4..1060202 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -9,8 +9,43 @@ - - - + + + + \ No newline at end of file From 417388eaedb7e4667f4b2b4e95b3d41db0df248c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 21:22:02 -0400 Subject: [PATCH 119/414] Add first buttons --- app/www/index.html | 77 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 1060202..c28e77e 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -6,9 +6,73 @@ html,body{ background: #212121; } + /* The switch - the box around the slider */ + .switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + } + + /* Hide default HTML checkbox */ + .switch input {display:none;} + + /* The slider */ + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + } + + .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + } + + input:checked + .slider { + background-color: #2196F3; + } + + input:focus + .slider { + box-shadow: 0 0 1px #2196F3; + } + + input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); + } + + /* Rounded sliders */ + .slider.round { + border-radius: 34px; + } + + .slider.round:before { + border-radius: 50%; + } + + + From d396fd2713f9d05e83c7a015c6ed07605a9d2d65 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 21:44:07 -0400 Subject: [PATCH 120/414] Style buttons and input --- app/www/index.html | 93 ++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index c28e77e..b12536b 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -19,60 +19,91 @@ /* The slider */ .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - -webkit-transition: .4s; - transition: .4s; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #20ce45; + -webkit-transition: .4s; + transition: .4s; } .slider:before { - position: absolute; - content: ""; - height: 26px; - width: 26px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; } input:checked + .slider { - background-color: #2196F3; + background-color: #f32121; } input:focus + .slider { - box-shadow: 0 0 1px #2196F3; + box-shadow: 0 0 1px #2196F3; } input:checked + .slider:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); } /* Rounded sliders */ .slider.round { - border-radius: 34px; + border-radius: 34px; } .slider.round:before { - border-radius: 50%; + border-radius: 50%; + } + input[type=number]{ + border: 1px solid #fff; + border-radius: 5px; + color: #fff; + font-size: 18px; + -webkit-appearance: none; + background: transparent; + box-shadow: none; + } + button{ + border: 1px solid #fff; + border-radius: 5px; + color: #fff; + font-size: 18px; + background: transparent; + -webkit-appearance: none; + box-shadow: none; + } + button:focus{ + background-color: #20ce45; + border-color: #20ce45; } - - - +
    + +
    +
    + +
    +
    + +
    +
    + +
    \ No newline at end of file From ccdae964ee9e4ad67ba1d967e7bdc7ff3a98d648 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 21 Oct 2017 22:01:46 -0400 Subject: [PATCH 122/414] Style the buttons and inputs to fill width of screen. --- app/www/index.html | 62 ++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 290e35e..da295f2 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -4,15 +4,23 @@ intval 3 - + -
    -
    Direction
    - -
    -
    -
    Exposure (ms)
    - -
    -
    -
    Delay (ms)
    - -
    -
    - -
    -
    - +
    +
    +
    Direction
    + +
    +
    +
    Exposure (ms)
    + +
    +
    +
    Delay (ms)
    + +
    +
    + +
    +
    + +
    From a9e13ac2a66eae98b8b1e639e28c3618b0ca55ed Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sun, 22 Oct 2017 19:59:38 -0400 Subject: [PATCH 131/414] Restore device state in app, set counter in webapp, --- app/www/index.html | 9 ++++++++- index.js | 31 +++++++++++++++++++++++++++++++ lib/intval/index.js | 19 ++++++++++++++++--- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index f9832ec..5967fb9 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -322,7 +322,13 @@ null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio" }; web.getStateSuccess = function (res) { 'use strict'; - console.dir(res); + if (res.frame.dir !== true) { + $('#dir').prop('checked', true); + setDirLabel(false); + } + $('#counter').val(res.counter); + $('#exposure').val(res.frame.exposure); + $('#delay').val(res.frame.delay); }; web.init = function () { 'use strict'; @@ -330,6 +336,7 @@ null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio" getState = web.getState; setDir = web.setDir; setExposure = web.setExposure; + web.getState(); }; web.init(); diff --git a/index.js b/index.js index 019cb99..34b68f9 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,8 @@ function createServer () { app.post('/dir', rDir) app.get( '/exposure', rExposure) app.post('/exposure', rExposure) + app.get( '/counter', rCounter) + app.post('/counter', rCounter) app.get( '/frame', rFrame) app.post('/frame', rFrame) app.get( '/sequence', () => {}) @@ -119,6 +121,35 @@ function rDelay (req, res, next) { return next() } +function rCounter (req, res, next) { + let counter = 0 + let set = false + if (req.query && typeof req.query.counter !== 'undefined') { + if (typeof req.query.counter === 'string') { + counter = parseInt(req.query.counter) + } else { + counter = req.query.counter + } + set = true + } + if (req.body && typeof req.body.counter !== 'undefined') { + if (typeof req.body.counter !== 'string') { + counter = parseInt(req.body.counter) + } else { + counter = req.body.counter + } + set = true + } + if (set) { + intval.setCounter(counter) + } else { + counter = intval._state.counter + } + log.info('/counter', { method : req.method, set : set, counter : counter }) + req.send({ counter : counter }) + return next() +} + function rFrame (req, res, next) { let dir = true let exposure = 0 diff --git a/lib/intval/index.js b/lib/intval/index.js index 3b9d54c..772a6cc 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -54,7 +54,8 @@ intval.init = function () { micro : { time : 0, primed : false //is ready to stop frame - } + }, + counter : 0 } intval._frame = { open : 250, //delay before pausing frame in open state @@ -264,6 +265,10 @@ intval.setDelay = function (val = 0) { intval._state.frame.delay = val log.info('setDelay', { delay : val }) } +intval.setCounter = function (val = 0) { + intval._state.counter = val + log.info('setCounter', { counter : val }) +} /** * Begin a single frame with set variables or defaults * @@ -313,8 +318,16 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { }, time + intval._frame.closed) } } - intval._state.frame.cb = (len) => { - cb(len) + if (dir) { + intval._state.frame.cb = (len) => { + intval._state.counter++ + cb(len) + } + } else { + intval._state.frame.cb = (len) => { + intval._state.counter-- + cb(len) + } } } /** From c92e4ec3d3866a73aba0e81a71befe53dbdb2437 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sun, 22 Oct 2017 20:01:43 -0400 Subject: [PATCH 132/414] Need to parseInt of counter value to increment properly --- app/www/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/www/index.html b/app/www/index.html index 5967fb9..de5bdb1 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -265,7 +265,7 @@ null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio" }; var incCounter = function (val) { var current = $('#counter').val(); - $('#counter').val(current + val); + $('#counter').val(parseInt(current) + val); }; var settingsPage = function () { $('#settings').show(); From 4a32fe3d60ea1494ae01dc7a466389514e97136e Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sun, 22 Oct 2017 20:04:12 -0400 Subject: [PATCH 133/414] Use 0 as default exposure in app. --- app/www/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/www/index.html b/app/www/index.html index de5bdb1..53038a9 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -215,7 +215,7 @@
    Exposure (ms)
    - +
    Delay (ms)
    From 6368ece015659d991c40c125bae08db433ca2bb7 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Mon, 23 Oct 2017 00:03:12 -0400 Subject: [PATCH 134/414] Set exposure, scale and parse time at different scales. --- app/www/index.html | 222 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 211 insertions(+), 11 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 53038a9..ea4737a 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -96,6 +96,7 @@ } input[type=number], input[type=text], + input[type=password], select{ width: 100%; border: 2px solid #fff; @@ -191,13 +192,27 @@ #bar h1.selected{ opacity: 1.0; } + #exposure, + #delay{ + width: 70%; + display: inline-block; + } + #scale, + #delayScale{ + width: 25%; + display: inline-block; + float: right; + } + #str{ + color: #fff; + }
    -

    INTVAL

    +

    INTVAL

    @@ -214,12 +229,24 @@
    -
    Exposure (ms)
    - +
    Exposure = 1/5
    + +
    -
    Delay (ms)
    +
    Delay
    +
    @@ -231,12 +258,21 @@

    SETTINGS

    -
    Bolex Model
    - + + +
    +
    +
    Wifi SSID
    + +
    +
    +
    Wifi Password
    + +
    +
    + +

    INTVAL

    + + +
    + + + - \ No newline at end of file diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js new file mode 100644 index 0000000..e2cec11 --- /dev/null +++ b/app/www/static/js/intval.mobile.js @@ -0,0 +1,17 @@ +'use strict'; +var mobile = {}; +mobile.init = function () { + frame = web.frame; + getState = web.getState; + setDir = web.setDir; + setExposure = web.setExposure; + setCounter = web.setCounter; + + mobile.getState(); +}; + +mobile.frame = function () {}; +mobile.getState = function () {}; +mobile.setDir = function () {}; +mobile.setExposure = function () {}; +mobile.setCounter = function () {}; \ No newline at end of file diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 84e0193..4cbd754 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -169,5 +169,4 @@ web.init = function () { setCounter = web.setCounter; web.getState(); -}; -web.init(); \ No newline at end of file +}; \ No newline at end of file From 4ccab7e933dca8c259981c526cbc5bea41c2f0d4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 12:25:06 -0500 Subject: [PATCH 211/414] Establish cordova app in /app directory. Will do development with testflight and using an adhoc profile for beta testers. --- app/config.xml | 26 +++++++++++++++++++++ app/hooks/README.md | 23 +++++++++++++++++++ app/package.json | 12 ++++++++++ app/res/README.md | 29 ++++++++++++++++++++++++ app/www/index.html | 2 ++ app/www/static/js/index.js | 46 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 app/config.xml create mode 100644 app/hooks/README.md create mode 100644 app/package.json create mode 100644 app/res/README.md create mode 100644 app/www/static/js/index.js diff --git a/app/config.xml b/app/config.xml new file mode 100644 index 0000000..8caf135 --- /dev/null +++ b/app/config.xml @@ -0,0 +1,26 @@ + + + INTVAL + + A sample Apache Cordova application that responds to the deviceready event. + + + Apache Cordova Team + + + + + + + + + + + + + + + + + + diff --git a/app/hooks/README.md b/app/hooks/README.md new file mode 100644 index 0000000..574ad4c --- /dev/null +++ b/app/hooks/README.md @@ -0,0 +1,23 @@ + +# Cordova Hooks + +Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide. diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..14a6739 --- /dev/null +++ b/app/package.json @@ -0,0 +1,12 @@ +{ + "name": "com.sixteenmillimeter.intval3", + "displayName": "INTVAL", + "version": "1.0.0", + "description": "A sample Apache Cordova application that responds to the deviceready event.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Apache Cordova Team", + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/app/res/README.md b/app/res/README.md new file mode 100644 index 0000000..bffb33b --- /dev/null +++ b/app/res/README.md @@ -0,0 +1,29 @@ + + +Note that these image resources are not copied into a project when a project +is created with the CLI. Although there are default image resources in a +newly-created project, those come from the platform-specific project template, +which can generally be found in the platform's `template` directory. Until +icon and splashscreen support is added to the CLI, these image resources +aren't used directly. + +See https://issues.apache.org/jira/browse/CB-5145 diff --git a/app/www/index.html b/app/www/index.html index fc0cb74..88696ca 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -255,9 +255,11 @@
    + + @@ -260,9 +96,5 @@ - \ No newline at end of file diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css new file mode 100644 index 0000000..416e249 --- /dev/null +++ b/app/www/static/css/index.css @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +* { + -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */ +} + +body { + -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ + -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ + -webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */ +} +html,body{ + background: #212121; + height: 100%; + padding: 0; + margin: 0; +} +*{ + color: #fff; + font-family: 'Arial Neue', Helvetical, Arial, sans-serif; + +} +body{ + padding-top: 15px; +} +.page{ + padding: 5px 10% 0 10%; + display: none; +} +.page.selected{ + display: block; +} +h2{ + font-size: 18px; + text-align: center; + font-weight: normal; +} +/* The switch - the box around the slider */ +.switch { + position: relative; + display: block; + width: 60px; + height: 34px; + margin: 0 auto; +} + +/* Hide default HTML checkbox */ +.switch input {display:none;} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #20ce45; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #f32121; +} + +input:focus + .slider { + +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} +input[type=number], +input[type=text], +input[type=password], +select{ + width: 100%; + border: 2px solid #fff; + border-radius: 5px; + color: #fff; + font-size: 18px; + -webkit-appearance: none; + background: transparent; + box-shadow: none; + outline: none; + margin: 5px 0; + padding: 5px 10px; + box-sizing: border-box; +} +option{ + color: #212121; +} +button{ + width: 100%; + border: 2px solid #fff; + border-radius: 5px; + color: #fff; + font-size: 18px; + background: transparent; + -webkit-appearance: none; + box-shadow: none; + outline: none; + margin: 5px 0; + padding: 5px 0; + text-align: center; +} +button:focus{ + background-color: #20ce45; + border-color: #20ce45; + color: #212121; + font-weight: bold; +} +.icon{ + display: block; + line-height: 34px; + height: 33px; + opacity: 0.5; + color: #fff; +} +.icon.selected{ + opacity: 1.0; +} +#fwdLabel,#bwdLabel{ + margin-top: 8px; +} +#fwdLabel{ + float: left; +} +#bwdLabel{ + position: absolute; + right: 10%; +} +.label{ + text-align: center; + color: #666; + margin-top: 6px; + margin-bottom: 9px; +} +#exposure, +#delay{ + width: 70%; + display: inline-block; +} +#scale, +#delayScale{ + width: 25%; + display: inline-block; + float: right; +} +#str{ + color: #fff; +} +#counter{ + text-align: center; +} + +#settingsIcon{ + background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDU0IDU0IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NCA1NDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPgo8Zz4KCTxwYXRoIGQ9Ik01MS4yMiwyMWgtNS4wNTJjLTAuODEyLDAtMS40ODEtMC40NDctMS43OTItMS4xOTdzLTAuMTUzLTEuNTQsMC40Mi0yLjExNGwzLjU3Mi0zLjU3MSAgIGMwLjUyNS0wLjUyNSwwLjgxNC0xLjIyNCwwLjgxNC0xLjk2NmMwLTAuNzQzLTAuMjg5LTEuNDQxLTAuODE0LTEuOTY3bC00LjU1My00LjU1M2MtMS4wNS0xLjA1LTIuODgxLTEuMDUyLTMuOTMzLDBsLTMuNTcxLDMuNTcxICAgYy0wLjU3NCwwLjU3My0xLjM2NiwwLjczMy0yLjExNCwwLjQyMUMzMy40NDcsOS4zMTMsMzMsOC42NDQsMzMsNy44MzJWMi43OEMzMywxLjI0NywzMS43NTMsMCwzMC4yMiwwSDIzLjc4ICAgQzIyLjI0NywwLDIxLDEuMjQ3LDIxLDIuNzh2NS4wNTJjMCwwLjgxMi0wLjQ0NywxLjQ4MS0xLjE5NywxLjc5MmMtMC43NDgsMC4zMTMtMS41NCwwLjE1Mi0yLjExNC0wLjQyMWwtMy41NzEtMy41NzEgICBjLTEuMDUyLTEuMDUyLTIuODgzLTEuMDUtMy45MzMsMGwtNC41NTMsNC41NTNjLTAuNTI1LDAuNTI1LTAuODE0LDEuMjI0LTAuODE0LDEuOTY3YzAsMC43NDIsMC4yODksMS40NCwwLjgxNCwxLjk2NmwzLjU3MiwzLjU3MSAgIGMwLjU3MywwLjU3NCwwLjczLDEuMzY0LDAuNDIsMi4xMTRTOC42NDQsMjEsNy44MzIsMjFIMi43OEMxLjI0NywyMSwwLDIyLjI0NywwLDIzLjc4djYuNDM5QzAsMzEuNzUzLDEuMjQ3LDMzLDIuNzgsMzNoNS4wNTIgICBjMC44MTIsMCwxLjQ4MSwwLjQ0NywxLjc5MiwxLjE5N3MwLjE1MywxLjU0LTAuNDIsMi4xMTRsLTMuNTcyLDMuNTcxYy0wLjUyNSwwLjUyNS0wLjgxNCwxLjIyNC0wLjgxNCwxLjk2NiAgIGMwLDAuNzQzLDAuMjg5LDEuNDQxLDAuODE0LDEuOTY3bDQuNTUzLDQuNTUzYzEuMDUxLDEuMDUxLDIuODgxLDEuMDUzLDMuOTMzLDBsMy41NzEtMy41NzJjMC41NzQtMC41NzMsMS4zNjMtMC43MzEsMi4xMTQtMC40MiAgIGMwLjc1LDAuMzExLDEuMTk3LDAuOTgsMS4xOTcsMS43OTJ2NS4wNTJjMCwxLjUzMywxLjI0NywyLjc4LDIuNzgsMi43OGg2LjQzOWMxLjUzMywwLDIuNzgtMS4yNDcsMi43OC0yLjc4di01LjA1MiAgIGMwLTAuODEyLDAuNDQ3LTEuNDgxLDEuMTk3LTEuNzkyYzAuNzUxLTAuMzEyLDEuNTQtMC4xNTMsMi4xMTQsMC40MmwzLjU3MSwzLjU3MmMxLjA1MiwxLjA1MiwyLjg4MywxLjA1LDMuOTMzLDBsNC41NTMtNC41NTMgICBjMC41MjUtMC41MjUsMC44MTQtMS4yMjQsMC44MTQtMS45NjdjMC0wLjc0Mi0wLjI4OS0xLjQ0LTAuODE0LTEuOTY2bC0zLjU3Mi0zLjU3MWMtMC41NzMtMC41NzQtMC43My0xLjM2NC0wLjQyLTIuMTE0ICAgUzQ1LjM1NiwzMyw0Ni4xNjgsMzNoNS4wNTJjMS41MzMsMCwyLjc4LTEuMjQ3LDIuNzgtMi43OFYyMy43OEM1NCwyMi4yNDcsNTIuNzUzLDIxLDUxLjIyLDIxeiBNNTIsMzAuMjIgICBDNTIsMzAuNjUsNTEuNjUsMzEsNTEuMjIsMzFoLTUuMDUyYy0xLjYyNCwwLTMuMDE5LDAuOTMyLTMuNjQsMi40MzJjLTAuNjIyLDEuNS0wLjI5NSwzLjE0NiwwLjg1NCw0LjI5NGwzLjU3MiwzLjU3MSAgIGMwLjMwNSwwLjMwNSwwLjMwNSwwLjgsMCwxLjEwNGwtNC41NTMsNC41NTNjLTAuMzA0LDAuMzA0LTAuNzk5LDAuMzA2LTEuMTA0LDBsLTMuNTcxLTMuNTcyYy0xLjE0OS0xLjE0OS0yLjc5NC0xLjQ3NC00LjI5NC0wLjg1NCAgIGMtMS41LDAuNjIxLTIuNDMyLDIuMDE2LTIuNDMyLDMuNjR2NS4wNTJDMzEsNTEuNjUsMzAuNjUsNTIsMzAuMjIsNTJIMjMuNzhDMjMuMzUsNTIsMjMsNTEuNjUsMjMsNTEuMjJ2LTUuMDUyICAgYzAtMS42MjQtMC45MzItMy4wMTktMi40MzItMy42NGMtMC41MDMtMC4yMDktMS4wMjEtMC4zMTEtMS41MzMtMC4zMTFjLTEuMDE0LDAtMS45OTcsMC40LTIuNzYxLDEuMTY0bC0zLjU3MSwzLjU3MiAgIGMtMC4zMDYsMC4zMDYtMC44MDEsMC4zMDQtMS4xMDQsMGwtNC41NTMtNC41NTNjLTAuMzA1LTAuMzA1LTAuMzA1LTAuOCwwLTEuMTA0bDMuNTcyLTMuNTcxYzEuMTQ4LTEuMTQ4LDEuNDc2LTIuNzk0LDAuODU0LTQuMjk0ICAgQzEwLjg1MSwzMS45MzIsOS40NTYsMzEsNy44MzIsMzFIMi43OEMyLjM1LDMxLDIsMzAuNjUsMiwzMC4yMlYyMy43OEMyLDIzLjM1LDIuMzUsMjMsMi43OCwyM2g1LjA1MiAgIGMxLjYyNCwwLDMuMDE5LTAuOTMyLDMuNjQtMi40MzJjMC42MjItMS41LDAuMjk1LTMuMTQ2LTAuODU0LTQuMjk0bC0zLjU3Mi0zLjU3MWMtMC4zMDUtMC4zMDUtMC4zMDUtMC44LDAtMS4xMDRsNC41NTMtNC41NTMgICBjMC4zMDQtMC4zMDUsMC43OTktMC4zMDUsMS4xMDQsMGwzLjU3MSwzLjU3MWMxLjE0NywxLjE0NywyLjc5MiwxLjQ3Niw0LjI5NCwwLjg1NEMyMi4wNjgsMTAuODUxLDIzLDkuNDU2LDIzLDcuODMyVjIuNzggICBDMjMsMi4zNSwyMy4zNSwyLDIzLjc4LDJoNi40MzlDMzAuNjUsMiwzMSwyLjM1LDMxLDIuNzh2NS4wNTJjMCwxLjYyNCwwLjkzMiwzLjAxOSwyLjQzMiwzLjY0ICAgYzEuNTAyLDAuNjIyLDMuMTQ2LDAuMjk0LDQuMjk0LTAuODU0bDMuNTcxLTMuNTcxYzAuMzA2LTAuMzA1LDAuODAxLTAuMzA1LDEuMTA0LDBsNC41NTMsNC41NTNjMC4zMDUsMC4zMDUsMC4zMDUsMC44LDAsMS4xMDQgICBsLTMuNTcyLDMuNTcxYy0xLjE0OCwxLjE0OC0xLjQ3NiwyLjc5NC0wLjg1NCw0LjI5NGMwLjYyMSwxLjUsMi4wMTYsMi40MzIsMy42NCwyLjQzMmg1LjA1MkM1MS42NSwyMyw1MiwyMy4zNSw1MiwyMy43OFYzMC4yMnoiIGZpbGw9IiNGRkZGRkYiLz4KCTxwYXRoIGQ9Ik0yNywxOGMtNC45NjMsMC05LDQuMDM3LTksOXM0LjAzNyw5LDksOXM5LTQuMDM3LDktOVMzMS45NjMsMTgsMjcsMTh6IE0yNywzNGMtMy44NTksMC03LTMuMTQxLTctN3MzLjE0MS03LDctNyAgIHM3LDMuMTQxLDcsN1MzMC44NTksMzQsMjcsMzR6IiBmaWxsPSIjRkZGRkZGIi8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==) no-repeat; +} +#mscriptIcon{ + background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjMycHgiIGhlaWdodD0iMzJweCIgdmlld0JveD0iMCAwIDUyMi40NjggNTIyLjQ2OSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTIyLjQ2OCA1MjIuNDY5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTMyNS43NjIsNzAuNTEzbC0xNy43MDYtNC44NTRjLTIuMjc5LTAuNzYtNC41MjQtMC41MjEtNi43MDcsMC43MTVjLTIuMTksMS4yMzctMy42NjksMy4wOTQtNC40MjksNS41NjhMMTkwLjQyNiw0NDAuNTMgICAgYy0wLjc2LDIuNDc1LTAuNTIyLDQuODA5LDAuNzE1LDYuOTk1YzEuMjM3LDIuMTksMy4wOSwzLjY2NSw1LjU2OCw0LjQyNWwxNy43MDEsNC44NTZjMi4yODQsMC43NjYsNC41MjEsMC41MjYsNi43MS0wLjcxMiAgICBjMi4xOS0xLjI0MywzLjY2Ni0zLjA5NCw0LjQyNS01LjU2NEwzMzIuMDQyLDgxLjkzNmMwLjc1OS0yLjQ3NCwwLjUyMy00LjgwOC0wLjcxNi02Ljk5OSAgICBDMzMwLjA4OCw3Mi43NDcsMzI4LjIzNyw3MS4yNzIsMzI1Ljc2Miw3MC41MTN6IiBmaWxsPSIjRkZGRkZGIi8+CgkJPHBhdGggZD0iTTE2Ni4xNjcsMTQyLjQ2NWMwLTIuNDc0LTAuOTUzLTQuNjY1LTIuODU2LTYuNTY3bC0xNC4yNzctMTQuMjc2Yy0xLjkwMy0xLjkwMy00LjA5My0yLjg1Ny02LjU2Ny0yLjg1NyAgICBzLTQuNjY1LDAuOTU1LTYuNTY3LDIuODU3TDIuODU2LDI1NC42NjZDMC45NSwyNTYuNTY5LDAsMjU4Ljc1OSwwLDI2MS4yMzNjMCwyLjQ3NCwwLjk1Myw0LjY2NCwyLjg1Niw2LjU2NmwxMzMuMDQzLDEzMy4wNDQgICAgYzEuOTAyLDEuOTA2LDQuMDg5LDIuODU0LDYuNTY3LDIuODU0czQuNjY1LTAuOTUxLDYuNTY3LTIuODU0bDE0LjI3Ny0xNC4yNjhjMS45MDMtMS45MDIsMi44NTYtNC4wOTMsMi44NTYtNi41NyAgICBjMC0yLjQ3MS0wLjk1My00LjY2MS0yLjg1Ni02LjU2M0w1MS4xMDcsMjYxLjIzM2wxMTIuMjA0LTExMi4yMDFDMTY1LjIxNywxNDcuMTMsMTY2LjE2NywxNDQuOTM5LDE2Ni4xNjcsMTQyLjQ2NXoiIGZpbGw9IiNGRkZGRkYiLz4KCQk8cGF0aCBkPSJNNTE5LjYxNCwyNTQuNjYzTDM4Ni41NjcsMTIxLjYxOWMtMS45MDItMS45MDItNC4wOTMtMi44NTctNi41NjMtMi44NTdjLTIuNDc4LDAtNC42NjEsMC45NTUtNi41NywyLjg1N2wtMTQuMjcxLDE0LjI3NSAgICBjLTEuOTAyLDEuOTAzLTIuODUxLDQuMDktMi44NTEsNi41NjdzMC45NDgsNC42NjUsMi44NTEsNi41NjdsMTEyLjIwNiwxMTIuMjA0TDM1OS4xNjMsMzczLjQ0MiAgICBjLTEuOTAyLDEuOTAyLTIuODUxLDQuMDkzLTIuODUxLDYuNTYzYzAsMi40NzgsMC45NDgsNC42NjgsMi44NTEsNi41N2wxNC4yNzEsMTQuMjY4YzEuOTA5LDEuOTA2LDQuMDkzLDIuODU0LDYuNTcsMi44NTQgICAgYzIuNDcxLDAsNC42NjEtMC45NTEsNi41NjMtMi44NTRMNTE5LjYxNCwyNjcuOGMxLjkwMy0xLjkwMiwyLjg1NC00LjA5NiwyLjg1NC02LjU3ICAgIEM1MjIuNDY4LDI1OC43NTUsNTIxLjUxNywyNTYuNTY1LDUxOS42MTQsMjU0LjY2M3oiIGZpbGw9IiNGRkZGRkYiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K) no-repeat; +} From 57aa86d4193e1189bc8ef08c4d6f9cd1041e8553 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 14:31:03 -0500 Subject: [PATCH 216/414] Install sqlite and squel for storing data embedded. --- package-lock.json | 735 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + 2 files changed, 725 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index f791a08..b070f16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "intval3", - "version": "0.0.1", + "version": "3.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -94,7 +94,6 @@ "resolved": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", "integrity": "sha1-IesK10O850eU45L0ph4TsHOT26o=", "requires": { - "bluetooth-hci-socket": "0.5.1", "bplist-parser": "0.0.6", "debug": "2.6.8" } @@ -105,16 +104,6 @@ "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", "dev": true }, - "bluetooth-hci-socket": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.1.tgz", - "integrity": "sha1-774hUk/Bz10/rl1RNl1WHUq77Qs=", - "optional": true, - "requires": { - "debug": "2.6.8", - "nan": "2.6.2" - } - }, "bplist-parser": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", @@ -1208,6 +1197,728 @@ "wbuf": "1.7.2" } }, + "sqlite3": { + "version": "3.1.13", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz", + "integrity": "sha512-JxXKPJnkZ6NuHRojq+g2WXWBt3M1G9sjZaYiHEWSTGijDM3cwju/0T2XbWqMXFmPqDgw+iB7zKQvnns4bvzXlw==", + "requires": { + "nan": "2.7.0", + "node-pre-gyp": "0.6.38" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "mime-db": { + "version": "1.30.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.17", + "bundled": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "nan": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" + }, + "node-pre-gyp": { + "version": "0.6.38", + "bundled": true, + "requires": { + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.4.1", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qs": { + "version": "6.4.0", + "bundled": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.3", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "semver": { + "version": "5.4.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "bundled": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.3", + "bundled": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.1.0", + "bundled": true + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "squel": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/squel/-/squel-5.12.0.tgz", + "integrity": "sha1-+NAxDbjSymO1w2wR3vwPeZP8944=" + }, "sshpk": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", diff --git a/package.json b/package.json index df5f968..3c9f0f0 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,8 @@ "node-persist": "^2.1.0", "onoff": "^1.1.5", "restify": "^5.2.0", + "sqlite3": "^3.1.13", + "squel": "^5.12.0", "uuid": "^3.1.0", "winston": "^2.3.1" }, From e1e272b92c8dcbac07aba7b8fe01e550742b4e27 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 14:32:13 -0500 Subject: [PATCH 217/414] Sorted out web/mobile startup. Working on ble features. --- app/www/static/js/index.js | 23 +++++++-------- app/www/static/js/intval.mobile.js | 46 +++++++++++++++++++++++++----- app/www/static/js/intval.web.js | 13 ++++----- 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/app/www/static/js/index.js b/app/www/static/js/index.js index 6963dc9..d7c73a0 100644 --- a/app/www/static/js/index.js +++ b/app/www/static/js/index.js @@ -20,6 +20,13 @@ var app = { // Application Constructor initialize: function() { document.addEventListener('deviceready', this.onDeviceReady.bind(this), false); + document.addEventListener('resume', this.onDeviceResume.bind(this), false); + document.addEventListener('DOMContentLoaded', event => { + if (typeof cordova === 'undefined') { + web.init(); + getState(); + } + }) }, // deviceready Event Handler @@ -27,19 +34,11 @@ var app = { // Bind any cordova events here. Common events are: // 'pause', 'resume', etc. onDeviceReady: function() { - this.receivedEvent('deviceready'); + mobile.init(); + getState(); }, - - // Update DOM on a Received Event - receivedEvent: function(id) { - var parentElement = document.getElementById(id); - var listeningElement = parentElement.querySelector('.listening'); - var receivedElement = parentElement.querySelector('.received'); - - listeningElement.setAttribute('style', 'display:none;'); - receivedElement.setAttribute('style', 'display:block;'); - - console.log('Received Event: ' + id); + onDeviceResume : function () { + getState(); } }; diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index e2cec11..a067145 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -1,13 +1,45 @@ 'use strict'; var mobile = {}; -mobile.init = function () { - frame = web.frame; - getState = web.getState; - setDir = web.setDir; - setExposure = web.setExposure; - setCounter = web.setCounter; - mobile.getState(); +mobile.ble = { + BLENO_DEVICE_NAME : 'intval3', + DEVICE_ID : 'intval3', + SERVICE_ID : '149582bd-d49d-4b5c-acd1-1ae503d09e7a', + CHAR_ID : '47bf69fb-f62f-4ef8-9be8-eb727a54fae4', //general data + WIFI_ID : '3fe7d9cf-7bd2-4ff0-97c5-ebe87288c2cc', //wifi only + DEVICES : [] +}; + +mobile.ble.scan = function () { + ble.scan([], 5, mobile.ble.onDiscover, BLE.onError); +}; + +mobile.ble.onDiscover = function (device) { + console.dir(device); + mobile.ble.connect(device.id); +} + +mobile.ble.connect = function (deviceId) { + ble.connect(deviceId, function (peripheral) { + mobile.ble.onConnect(peripheral, deviceId); + }, mobile.ble.onError); +}; + +mobile.ble.onConnect = function (peripheral, deviceId) { + console.log(peripheral); + console.log(deviceId); +}; + +mobile.ble.onError = function (err) { + alert(JSON.stringify(err)); +}; + +mobile.init = function () { + frame = mobile.frame; + getState = mobile.getState; + setDir = mobile.setDir; + setExposure = mobile.setExposure; + setCounter = mobile.setCounter; }; mobile.frame = function () {}; diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 86e495b..0585796 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -162,11 +162,10 @@ web.useJson = function (res) { return res.json(); } web.init = function () { - window.frame = web.frame; - window.getState = web.getState; - window.setDir = web.setDir; - window.setExposure = web.setExposure; - window.setCounter = web.setCounter; - - getState(); + //window.frame = web.frame; + //window.getState = web.getState; + //window.setDir = web.setDir; + //window.setExposure = web.setExposure; + //window.setCounter = web.setCounter; + console.log('started web') }; \ No newline at end of file From 3aae58c5d984be0ee41cdedf53e5778595ef0cc3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 19:48:45 -0500 Subject: [PATCH 218/414] Add mscript GUI. --- app/www/index.html | 28 ++++++++----- app/www/static/css/index.css | 65 +++++++++++++++++++++++------ app/www/static/css/monokai.css | 36 ++++++++++++++++ app/www/static/js/intval.core.js | 8 ++-- app/www/static/js/intval.mscript.js | 19 +++++++++ 5 files changed, 129 insertions(+), 27 deletions(-) create mode 100644 app/www/static/css/monokai.css create mode 100644 app/www/static/js/intval.mscript.js diff --git a/app/www/index.html b/app/www/index.html index 7c1312a..5088dec 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -1,14 +1,17 @@ - intval 3 + INTVAL + +
    +

    INTVAL

    Counter
    @@ -72,29 +75,32 @@
    -

    Sequence

    +

    SEQUENCER

    -

    mscript

    +

    MSCRIPT

    +
    -
    -
    +
    +
    -
    -

    INTVAL

    +
    +
    -
    - +
    +
    -
    -
    +
    +
    + + \ No newline at end of file diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 416e249..0e9b9a1 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -31,7 +31,7 @@ html,body{ padding: 0; margin: 0; } -*{ +html,body,button,h2,label,input{ color: #fff; font-family: 'Arial Neue', Helvetical, Arial, sans-serif; @@ -150,16 +150,7 @@ button:focus{ color: #212121; font-weight: bold; } -.icon{ - display: block; - line-height: 34px; - height: 33px; - opacity: 0.5; - color: #fff; -} -.icon.selected{ - opacity: 1.0; -} + #fwdLabel,#bwdLabel{ margin-top: 8px; } @@ -194,9 +185,57 @@ button:focus{ text-align: center; } -#settingsIcon{ +#frame{ + padding: 10px 0; + font-size: 24px; +} + +#settingsIcon > div{ background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDU0IDU0IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NCA1NDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPgo8Zz4KCTxwYXRoIGQ9Ik01MS4yMiwyMWgtNS4wNTJjLTAuODEyLDAtMS40ODEtMC40NDctMS43OTItMS4xOTdzLTAuMTUzLTEuNTQsMC40Mi0yLjExNGwzLjU3Mi0zLjU3MSAgIGMwLjUyNS0wLjUyNSwwLjgxNC0xLjIyNCwwLjgxNC0xLjk2NmMwLTAuNzQzLTAuMjg5LTEuNDQxLTAuODE0LTEuOTY3bC00LjU1My00LjU1M2MtMS4wNS0xLjA1LTIuODgxLTEuMDUyLTMuOTMzLDBsLTMuNTcxLDMuNTcxICAgYy0wLjU3NCwwLjU3My0xLjM2NiwwLjczMy0yLjExNCwwLjQyMUMzMy40NDcsOS4zMTMsMzMsOC42NDQsMzMsNy44MzJWMi43OEMzMywxLjI0NywzMS43NTMsMCwzMC4yMiwwSDIzLjc4ICAgQzIyLjI0NywwLDIxLDEuMjQ3LDIxLDIuNzh2NS4wNTJjMCwwLjgxMi0wLjQ0NywxLjQ4MS0xLjE5NywxLjc5MmMtMC43NDgsMC4zMTMtMS41NCwwLjE1Mi0yLjExNC0wLjQyMWwtMy41NzEtMy41NzEgICBjLTEuMDUyLTEuMDUyLTIuODgzLTEuMDUtMy45MzMsMGwtNC41NTMsNC41NTNjLTAuNTI1LDAuNTI1LTAuODE0LDEuMjI0LTAuODE0LDEuOTY3YzAsMC43NDIsMC4yODksMS40NCwwLjgxNCwxLjk2NmwzLjU3MiwzLjU3MSAgIGMwLjU3MywwLjU3NCwwLjczLDEuMzY0LDAuNDIsMi4xMTRTOC42NDQsMjEsNy44MzIsMjFIMi43OEMxLjI0NywyMSwwLDIyLjI0NywwLDIzLjc4djYuNDM5QzAsMzEuNzUzLDEuMjQ3LDMzLDIuNzgsMzNoNS4wNTIgICBjMC44MTIsMCwxLjQ4MSwwLjQ0NywxLjc5MiwxLjE5N3MwLjE1MywxLjU0LTAuNDIsMi4xMTRsLTMuNTcyLDMuNTcxYy0wLjUyNSwwLjUyNS0wLjgxNCwxLjIyNC0wLjgxNCwxLjk2NiAgIGMwLDAuNzQzLDAuMjg5LDEuNDQxLDAuODE0LDEuOTY3bDQuNTUzLDQuNTUzYzEuMDUxLDEuMDUxLDIuODgxLDEuMDUzLDMuOTMzLDBsMy41NzEtMy41NzJjMC41NzQtMC41NzMsMS4zNjMtMC43MzEsMi4xMTQtMC40MiAgIGMwLjc1LDAuMzExLDEuMTk3LDAuOTgsMS4xOTcsMS43OTJ2NS4wNTJjMCwxLjUzMywxLjI0NywyLjc4LDIuNzgsMi43OGg2LjQzOWMxLjUzMywwLDIuNzgtMS4yNDcsMi43OC0yLjc4di01LjA1MiAgIGMwLTAuODEyLDAuNDQ3LTEuNDgxLDEuMTk3LTEuNzkyYzAuNzUxLTAuMzEyLDEuNTQtMC4xNTMsMi4xMTQsMC40MmwzLjU3MSwzLjU3MmMxLjA1MiwxLjA1MiwyLjg4MywxLjA1LDMuOTMzLDBsNC41NTMtNC41NTMgICBjMC41MjUtMC41MjUsMC44MTQtMS4yMjQsMC44MTQtMS45NjdjMC0wLjc0Mi0wLjI4OS0xLjQ0LTAuODE0LTEuOTY2bC0zLjU3Mi0zLjU3MWMtMC41NzMtMC41NzQtMC43My0xLjM2NC0wLjQyLTIuMTE0ICAgUzQ1LjM1NiwzMyw0Ni4xNjgsMzNoNS4wNTJjMS41MzMsMCwyLjc4LTEuMjQ3LDIuNzgtMi43OFYyMy43OEM1NCwyMi4yNDcsNTIuNzUzLDIxLDUxLjIyLDIxeiBNNTIsMzAuMjIgICBDNTIsMzAuNjUsNTEuNjUsMzEsNTEuMjIsMzFoLTUuMDUyYy0xLjYyNCwwLTMuMDE5LDAuOTMyLTMuNjQsMi40MzJjLTAuNjIyLDEuNS0wLjI5NSwzLjE0NiwwLjg1NCw0LjI5NGwzLjU3MiwzLjU3MSAgIGMwLjMwNSwwLjMwNSwwLjMwNSwwLjgsMCwxLjEwNGwtNC41NTMsNC41NTNjLTAuMzA0LDAuMzA0LTAuNzk5LDAuMzA2LTEuMTA0LDBsLTMuNTcxLTMuNTcyYy0xLjE0OS0xLjE0OS0yLjc5NC0xLjQ3NC00LjI5NC0wLjg1NCAgIGMtMS41LDAuNjIxLTIuNDMyLDIuMDE2LTIuNDMyLDMuNjR2NS4wNTJDMzEsNTEuNjUsMzAuNjUsNTIsMzAuMjIsNTJIMjMuNzhDMjMuMzUsNTIsMjMsNTEuNjUsMjMsNTEuMjJ2LTUuMDUyICAgYzAtMS42MjQtMC45MzItMy4wMTktMi40MzItMy42NGMtMC41MDMtMC4yMDktMS4wMjEtMC4zMTEtMS41MzMtMC4zMTFjLTEuMDE0LDAtMS45OTcsMC40LTIuNzYxLDEuMTY0bC0zLjU3MSwzLjU3MiAgIGMtMC4zMDYsMC4zMDYtMC44MDEsMC4zMDQtMS4xMDQsMGwtNC41NTMtNC41NTNjLTAuMzA1LTAuMzA1LTAuMzA1LTAuOCwwLTEuMTA0bDMuNTcyLTMuNTcxYzEuMTQ4LTEuMTQ4LDEuNDc2LTIuNzk0LDAuODU0LTQuMjk0ICAgQzEwLjg1MSwzMS45MzIsOS40NTYsMzEsNy44MzIsMzFIMi43OEMyLjM1LDMxLDIsMzAuNjUsMiwzMC4yMlYyMy43OEMyLDIzLjM1LDIuMzUsMjMsMi43OCwyM2g1LjA1MiAgIGMxLjYyNCwwLDMuMDE5LTAuOTMyLDMuNjQtMi40MzJjMC42MjItMS41LDAuMjk1LTMuMTQ2LTAuODU0LTQuMjk0bC0zLjU3Mi0zLjU3MWMtMC4zMDUtMC4zMDUtMC4zMDUtMC44LDAtMS4xMDRsNC41NTMtNC41NTMgICBjMC4zMDQtMC4zMDUsMC43OTktMC4zMDUsMS4xMDQsMGwzLjU3MSwzLjU3MWMxLjE0NywxLjE0NywyLjc5MiwxLjQ3Niw0LjI5NCwwLjg1NEMyMi4wNjgsMTAuODUxLDIzLDkuNDU2LDIzLDcuODMyVjIuNzggICBDMjMsMi4zNSwyMy4zNSwyLDIzLjc4LDJoNi40MzlDMzAuNjUsMiwzMSwyLjM1LDMxLDIuNzh2NS4wNTJjMCwxLjYyNCwwLjkzMiwzLjAxOSwyLjQzMiwzLjY0ICAgYzEuNTAyLDAuNjIyLDMuMTQ2LDAuMjk0LDQuMjk0LTAuODU0bDMuNTcxLTMuNTcxYzAuMzA2LTAuMzA1LDAuODAxLTAuMzA1LDEuMTA0LDBsNC41NTMsNC41NTNjMC4zMDUsMC4zMDUsMC4zMDUsMC44LDAsMS4xMDQgICBsLTMuNTcyLDMuNTcxYy0xLjE0OCwxLjE0OC0xLjQ3NiwyLjc5NC0wLjg1NCw0LjI5NGMwLjYyMSwxLjUsMi4wMTYsMi40MzIsMy42NCwyLjQzMmg1LjA1MkM1MS42NSwyMyw1MiwyMy4zNSw1MiwyMy43OFYzMC4yMnoiIGZpbGw9IiNGRkZGRkYiLz4KCTxwYXRoIGQ9Ik0yNywxOGMtNC45NjMsMC05LDQuMDM3LTksOXM0LjAzNyw5LDksOXM5LTQuMDM3LDktOVMzMS45NjMsMTgsMjcsMTh6IE0yNywzNGMtMy44NTksMC03LTMuMTQxLTctN3MzLjE0MS03LDctNyAgIHM3LDMuMTQxLDcsN1MzMC44NTksMzQsMjcsMzR6IiBmaWxsPSIjRkZGRkZGIi8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==) no-repeat; } -#mscriptIcon{ +#mscriptIcon > div{ background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjMycHgiIGhlaWdodD0iMzJweCIgdmlld0JveD0iMCAwIDUyMi40NjggNTIyLjQ2OSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTIyLjQ2OCA1MjIuNDY5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTMyNS43NjIsNzAuNTEzbC0xNy43MDYtNC44NTRjLTIuMjc5LTAuNzYtNC41MjQtMC41MjEtNi43MDcsMC43MTVjLTIuMTksMS4yMzctMy42NjksMy4wOTQtNC40MjksNS41NjhMMTkwLjQyNiw0NDAuNTMgICAgYy0wLjc2LDIuNDc1LTAuNTIyLDQuODA5LDAuNzE1LDYuOTk1YzEuMjM3LDIuMTksMy4wOSwzLjY2NSw1LjU2OCw0LjQyNWwxNy43MDEsNC44NTZjMi4yODQsMC43NjYsNC41MjEsMC41MjYsNi43MS0wLjcxMiAgICBjMi4xOS0xLjI0MywzLjY2Ni0zLjA5NCw0LjQyNS01LjU2NEwzMzIuMDQyLDgxLjkzNmMwLjc1OS0yLjQ3NCwwLjUyMy00LjgwOC0wLjcxNi02Ljk5OSAgICBDMzMwLjA4OCw3Mi43NDcsMzI4LjIzNyw3MS4yNzIsMzI1Ljc2Miw3MC41MTN6IiBmaWxsPSIjRkZGRkZGIi8+CgkJPHBhdGggZD0iTTE2Ni4xNjcsMTQyLjQ2NWMwLTIuNDc0LTAuOTUzLTQuNjY1LTIuODU2LTYuNTY3bC0xNC4yNzctMTQuMjc2Yy0xLjkwMy0xLjkwMy00LjA5My0yLjg1Ny02LjU2Ny0yLjg1NyAgICBzLTQuNjY1LDAuOTU1LTYuNTY3LDIuODU3TDIuODU2LDI1NC42NjZDMC45NSwyNTYuNTY5LDAsMjU4Ljc1OSwwLDI2MS4yMzNjMCwyLjQ3NCwwLjk1Myw0LjY2NCwyLjg1Niw2LjU2NmwxMzMuMDQzLDEzMy4wNDQgICAgYzEuOTAyLDEuOTA2LDQuMDg5LDIuODU0LDYuNTY3LDIuODU0czQuNjY1LTAuOTUxLDYuNTY3LTIuODU0bDE0LjI3Ny0xNC4yNjhjMS45MDMtMS45MDIsMi44NTYtNC4wOTMsMi44NTYtNi41NyAgICBjMC0yLjQ3MS0wLjk1My00LjY2MS0yLjg1Ni02LjU2M0w1MS4xMDcsMjYxLjIzM2wxMTIuMjA0LTExMi4yMDFDMTY1LjIxNywxNDcuMTMsMTY2LjE2NywxNDQuOTM5LDE2Ni4xNjcsMTQyLjQ2NXoiIGZpbGw9IiNGRkZGRkYiLz4KCQk8cGF0aCBkPSJNNTE5LjYxNCwyNTQuNjYzTDM4Ni41NjcsMTIxLjYxOWMtMS45MDItMS45MDItNC4wOTMtMi44NTctNi41NjMtMi44NTdjLTIuNDc4LDAtNC42NjEsMC45NTUtNi41NywyLjg1N2wtMTQuMjcxLDE0LjI3NSAgICBjLTEuOTAyLDEuOTAzLTIuODUxLDQuMDktMi44NTEsNi41NjdzMC45NDgsNC42NjUsMi44NTEsNi41NjdsMTEyLjIwNiwxMTIuMjA0TDM1OS4xNjMsMzczLjQ0MiAgICBjLTEuOTAyLDEuOTAyLTIuODUxLDQuMDkzLTIuODUxLDYuNTYzYzAsMi40NzgsMC45NDgsNC42NjgsMi44NTEsNi41N2wxNC4yNzEsMTQuMjY4YzEuOTA5LDEuOTA2LDQuMDkzLDIuODU0LDYuNTcsMi44NTQgICAgYzIuNDcxLDAsNC42NjEtMC45NTEsNi41NjMtMi44NTRMNTE5LjYxNCwyNjcuOGMxLjkwMy0xLjkwMiwyLjg1NC00LjA5NiwyLjg1NC02LjU3ICAgIEM1MjIuNDY4LDI1OC43NTUsNTIxLjUxNywyNTYuNTY1LDUxOS42MTQsMjU0LjY2M3oiIGZpbGw9IiNGRkZGRkYiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K) no-repeat; } +#appIcon > div{ + background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDYwIDYwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2MCA2MDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPgo8Zz4KCTxwYXRoIGQ9Ik00NS41NjMsMjkuMTc0bC0yMi0xNWMtMC4zMDctMC4yMDgtMC43MDMtMC4yMzEtMS4wMzEtMC4wNThDMjIuMjA1LDE0LjI4OSwyMiwxNC42MjksMjIsMTV2MzAgICBjMCwwLjM3MSwwLjIwNSwwLjcxMSwwLjUzMywwLjg4NEMyMi42NzksNDUuOTYyLDIyLjg0LDQ2LDIzLDQ2YzAuMTk3LDAsMC4zOTQtMC4wNTksMC41NjMtMC4xNzRsMjItMTUgICBDNDUuODM2LDMwLjY0LDQ2LDMwLjMzMSw0NiwzMFM0NS44MzYsMjkuMzYsNDUuNTYzLDI5LjE3NHogTTI0LDQzLjEwN1YxNi44OTNMNDMuMjI1LDMwTDI0LDQzLjEwN3oiIGZpbGw9IiNGRkZGRkYiLz4KCTxwYXRoIGQ9Ik0zMCwwQzEzLjQ1OCwwLDAsMTMuNDU4LDAsMzBzMTMuNDU4LDMwLDMwLDMwczMwLTEzLjQ1OCwzMC0zMFM0Ni41NDIsMCwzMCwweiBNMzAsNThDMTQuNTYxLDU4LDIsNDUuNDM5LDIsMzAgICBTMTQuNTYxLDIsMzAsMnMyOCwxMi41NjEsMjgsMjhTNDUuNDM5LDU4LDMwLDU4eiIgZmlsbD0iI0ZGRkZGRiIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat; +} +#sequencerIcon > div{ + background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAyOTAgMjkwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyOTAgMjkwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjMycHgiIGhlaWdodD0iMzJweCI+CjxnPgoJPHJlY3QgeT0iMjIwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHk9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgeT0iMjIwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgeT0iMTEwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHk9IjIyMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHk9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHdpZHRoPSI3MCIgaGVpZ2h0PSI3MCIgZmlsbD0iI0ZGRkZGRiIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat; +} + +footer{ + width: 100%; + height: 50px; + position: fixed; + bottom: 0; + border-top: 1px solid rgba(255, 255, 255, 0.3); +} + +footer .icon { + width: 25%; + height: 50px; + float: left; + box-sizing: border-box; + border-right: 1px solid rgba(255, 255, 255, 0.3); +} + +.icon > div{ + display: block; + line-height: 34px; + height: 33px; + width: 33px; + opacity: 0.5; + color: #fff; + margin: 10px auto 0; +} +.icon.selected > div{ + opacity: 1.0; +} + +footer > div.selected{ + background: rgba(255, 255, 255, 0.1); +} + +.CodeMirror{ + font-size: 18px; +} diff --git a/app/www/static/css/monokai.css b/app/www/static/css/monokai.css new file mode 100644 index 0000000..b5edd13 --- /dev/null +++ b/app/www/static/css/monokai.css @@ -0,0 +1,36 @@ +/* Based on Sublime Text's Monokai theme */ + +.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; } +.cm-s-monokai div.CodeMirror-selected { background: #49483E; } +.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); } +.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); } +.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; } +.cm-s-monokai .CodeMirror-guttermarker { color: white; } +.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-monokai span.cm-comment { color: #75715e; } +.cm-s-monokai span.cm-atom { color: #ae81ff; } +.cm-s-monokai span.cm-number { color: #ae81ff; } + +.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; } +.cm-s-monokai span.cm-keyword { color: #f92672; } +.cm-s-monokai span.cm-builtin { color: #66d9ef; } +.cm-s-monokai span.cm-string { color: #e6db74; } + +.cm-s-monokai span.cm-variable { color: #f8f8f2; } +.cm-s-monokai span.cm-variable-2 { color: #9effff; } +.cm-s-monokai span.cm-variable-3, .cm-s-monokai span.cm-type { color: #66d9ef; } +.cm-s-monokai span.cm-def { color: #fd971f; } +.cm-s-monokai span.cm-bracket { color: #f8f8f2; } +.cm-s-monokai span.cm-tag { color: #f92672; } +.cm-s-monokai span.cm-header { color: #ae81ff; } +.cm-s-monokai span.cm-link { color: #ae81ff; } +.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; } + +.cm-s-monokai .CodeMirror-activeline-background { background: #373831; } +.cm-s-monokai .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 6c77e3e..3fdc059 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -128,14 +128,15 @@ var forceCounter = function (val) { document.getElementById('counter').value = val; } var unsetPages = function () { - const pages = document.getElementsByClassName('page'); - const icons = document.getElementsByClassName('icon'); + const pages = document.querySelectorAll('.page'); + const icons = document.querySelectorAll('.icon'); for (let icon of icons) { if (icon.classList.contains('selected')) icon.classList.remove('selected'); - } + }; for (let page of pages){; if (page.classList.contains('selected')) page.classList.remove('selected'); } + }; var appPage = function () { unsetPages(); @@ -156,6 +157,7 @@ var mscriptPage = function () { unsetPages(); document.getElementById('mscript').classList.add('selected'); document.getElementById('mscriptIcon').classList.add('selected'); + editor.cm.refresh(); }; var isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); diff --git a/app/www/static/js/intval.mscript.js b/app/www/static/js/intval.mscript.js new file mode 100644 index 0000000..aa04b33 --- /dev/null +++ b/app/www/static/js/intval.mscript.js @@ -0,0 +1,19 @@ +'use strict' +const editor = {} + +editor.init = function () { + const elem = document.getElementById('mscript_editor'); + editor.cm = CodeMirror.fromTextArea(elem, { + lineNumbers: true, + theme : 'monokai', + gutters: ['CodeMirror-linenumbers'] + }); + setTimeout(() => { + editor.cm.setValue('CF'); + editor.cm.refresh(); + }, 10); +}; + +document.addEventListener('DOMContentLoaded', event => { + editor.init(); +}); \ No newline at end of file From e6b0329aa1af762d04f4b47d4e8d33d858f6fe58 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 19:49:06 -0500 Subject: [PATCH 219/414] Add codemirror dependencies. --- app/www/static/css/codemirror.css | 346 ++ app/www/static/js/codemirror.js | 9665 +++++++++++++++++++++++++++++ 2 files changed, 10011 insertions(+) create mode 100644 app/www/static/css/codemirror.css create mode 100644 app/www/static/js/codemirror.js diff --git a/app/www/static/css/codemirror.css b/app/www/static/css/codemirror.css new file mode 100644 index 0000000..8f4f22f --- /dev/null +++ b/app/www/static/css/codemirror.css @@ -0,0 +1,346 @@ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} +.cm-fat-cursor-mark { + background-color: rgba(20, 255, 20, 0.5); + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; +} +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: -20px; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; margin-right: -30px; + padding-bottom: 30px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -30px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + overflow: auto; +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } diff --git a/app/www/static/js/codemirror.js b/app/www/static/js/codemirror.js new file mode 100644 index 0000000..8a69129 --- /dev/null +++ b/app/www/static/js/codemirror.js @@ -0,0 +1,9665 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// This is CodeMirror (http://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + +// Kludges for bugs and behavior differences that can't be feature +// detected are enabled based on userAgent etc sniffing. +var userAgent = navigator.userAgent; +var platform = navigator.platform; + +var gecko = /gecko\/\d/i.test(userAgent); +var ie_upto10 = /MSIE \d/.test(userAgent); +var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); +var edge = /Edge\/(\d+)/.exec(userAgent); +var ie = ie_upto10 || ie_11up || edge; +var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); +var webkit = !edge && /WebKit\//.test(userAgent); +var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); +var chrome = !edge && /Chrome\//.test(userAgent); +var presto = /Opera\//.test(userAgent); +var safari = /Apple Computer/.test(navigator.vendor); +var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); +var phantom = /PhantomJS/.test(userAgent); + +var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); +var android = /Android/.test(userAgent); +// This is woefully incomplete. Suggestions for alternative methods welcome. +var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); +var mac = ios || /Mac/.test(platform); +var chromeOS = /\bCrOS\b/.test(userAgent); +var windows = /win/i.test(platform); + +var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); +if (presto_version) { presto_version = Number(presto_version[1]); } +if (presto_version && presto_version >= 15) { presto = false; webkit = true; } +// Some browsers use the wrong event properties to signal cmd/ctrl on OS X +var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); +var captureRightClick = gecko || (ie && ie_version >= 9); + +function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + +var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } +}; + +function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e +} + +function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) +} + +function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e +} +// wrapper for elt, which removes the elt from the accessibility tree +function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e +} + +var range; +if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r +}; } +else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r +}; } + +function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) +} + +function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement +} + +function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } +} +function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b +} + +var selectInput = function(node) { node.select(); }; +if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } +else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + +function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} +} + +function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target +} + +// Counts the column offset in a string, taking tabs into account. +// Used mostly to find indentation. +function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } +} + +var Delayed = function() {this.id = null;}; +Delayed.prototype.set = function (ms, f) { + clearTimeout(this.id); + this.id = setTimeout(f, ms); +}; + +function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 +} + +// Number of pixels added to scroller and sizer to hide scrollbar +var scrollerGap = 30; + +// Returned or thrown by various protocols to signal 'I'm not +// handling this'. +var Pass = {toString: function(){return "CodeMirror.Pass"}}; + +// Reused option objects for setSelection & friends +var sel_dontScroll = {scroll: false}; +var sel_mouse = {origin: "*mouse"}; +var sel_move = {origin: "+move"}; + +// The inverse of countColumn -- find the offset that corresponds to +// a particular column. +function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } +} + +var spaceStrs = [""]; +function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] +} + +function lst(arr) { return arr[arr.length-1] } + +function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out +} + +function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); +} + +function nothing() {} + +function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst +} + +var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; +function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) +} +function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) +} + +function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true +} + +// Extending unicode characters. A series of a non-extending char + +// any number of extending chars is treated as a single unit as far +// as editing and measuring is concerned. This is not fully correct, +// since some scripts/fonts/browsers also treat other configurations +// of code points as a group. +var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; +function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + +// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. +function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos +} + +// Returns the value from the range [`from`; `to`] that satisfies +// `pred` and is closest to `from`. Assumes that at least `to` +// satisfies `pred`. Supports `from` being greater than `to`. +function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } +} + +// The display handles the DOM integration, both for input reading +// and content drawing. It holds references to DOM nodes and +// display-related state. + +function Display(place, doc, input) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + input.init(d); +} + +// Find the line object corresponding to the given line number. +function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] +} + +// Get the part of a document between two positions, as an array of +// strings. +function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out +} +// Get the lines between from and to, as array of strings. +function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out +} + +// Update the height of a line, propagating the height change +// upwards to parent nodes. +function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } +} + +// Given a line object, find its line number by walking up through +// its parent links. +function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first +} + +// Find the line at the given vertical position, using the height +// information in the document tree. +function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i +} + +function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + +function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) +} + +// A Pos instance represents a position within the text. +function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; +} + +// Compare two positions, return 0 if they are the same, a negative +// number when a is less, and a positive number otherwise. +function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + +function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + +function copyPos(x) {return Pos(x.line, x.ch)} +function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } +function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + +// Most of the external API clips given positions to make sure they +// actually exist within the document. +function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} +function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) +} +function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } +} +function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out +} + +// Optimize some code when these features are not used. +var sawReadOnlySpans = false; +var sawCollapsedSpans = false; + +function seeReadOnlySpans() { + sawReadOnlySpans = true; +} + +function seeCollapsedSpans() { + sawCollapsedSpans = true; +} + +// TEXTMARKER SPANS + +function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; +} + +// Search an array of spans for a span matching the given marker. +function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } +} +// Remove a span from an array, returning undefined if no spans are +// left (we don't store arrays for lines without spans). +function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r +} +// Add a span to a line. +function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); +} + +// Used for the algorithm that adjusts markers for a change in the +// document. These functions cut an array of spans at a given +// character position, returning an array of remaining chunks (or +// undefined if nothing remains). +function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw +} +function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw +} + +// Given a change object, compute the new set of marker spans that +// cover the line in which the change took place. Removes spans +// entirely within the change, reconnects spans belonging to the +// same marker that appear on both sides of the change, and cuts off +// spans partially within the change. Returns an array of span +// arrays with one element for each line in (after) the change. +function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers +} + +// Remove spans that are empty and don't have a clearWhenEmpty +// option of false. +function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans +} + +// Used to 'clip' out readOnly ranges when making a change. +function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts +} + +// Connect or disconnect spans from a line. +function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; +} +function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; +} + +// Helpers used when computing which overlapping collapsed span +// counts as the larger one. +function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } +function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + +// Returns a number indicating which of two overlapping collapsed +// spans is larger (and thus includes the other). Falls back to +// comparing ids when the spans cover exactly the same range. +function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id +} + +// Find out whether a line ends or starts in a collapsed span. If +// so, return the marker for that span. +function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found +} +function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } +function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + +// Test whether there exists a collapsed span that partially +// overlaps (covers the start or end, but not both) of a new span. +// Such overlap is not allowed. +function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { + var line = getLine(doc, lineNo$$1); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } +} + +// A visual line is a line as drawn on the screen. Folding, for +// example, can cause multiple logical lines to appear on the same +// visual line. This finds the start of the visual line that the +// given line is part of (usually that is the line itself). +function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line +} + +function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line +} + +// Returns an array of logical lines that continue the visual line +// started by the argument, or undefined if there are no such lines. +function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines +} + +// Get the line number of the start of the visual line that the +// given line number is part of. +function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) +} + +// Get the line number of the start of the next visual line after +// the given line. +function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 +} + +// Compute whether a line is hidden. Lines count as hidden when they +// are part of a visual line that starts with another line, or when +// they are entirely covered by collapsed, non-widget span. +function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } +} +function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } +} + +// Find the height above the given line. +function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h +} + +// Compute the character length of a line, taking into account +// collapsed ranges (see markText) that might hide parts, and join +// other lines onto it. +function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len +} + +// Find the longest line in the document. +function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); +} + +// BIDI HELPERS + +function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } +} + +var bidiOther = null; +function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther +} + +// Bidirectional ordering algorithm +// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm +// that this (partially) implements. + +// One-char codes used for character types: +// L (L): Left-to-Right +// R (R): Right-to-Left +// r (AL): Right-to-Left Arabic +// 1 (EN): European Number +// + (ES): European Number Separator +// % (ET): European Number Terminator +// n (AN): Arabic Number +// , (CS): Common Number Separator +// m (NSM): Non-Spacing Mark +// b (BN): Boundary Neutral +// s (B): Paragraph Separator +// t (S): Segment Separator +// w (WS): Whitespace +// N (ON): Other Neutrals + +// Returns null if characters are ordered as they appear +// (left-to-right), or an array of sections ({from, to, level} +// objects) in the order in which they occur visually. +var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } +})(); + +// Get the bidi ordering for the given line (and cache it). Returns +// false for lines that are fully left-to-right, and an array of +// BidiSpan objects otherwise. +function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order +} + +// EVENT HANDLING + +// Lightweight event framework. on/off also work on DOM nodes, +// registering native DOM handlers. + +var noHandlers = []; + +var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map$$1 = emitter._handlers || (emitter._handlers = {}); + map$$1[type] = (map$$1[type] || noHandlers).concat(f); + } +}; + +function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers +} + +function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } +} + +function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } +} + +// The DOM events that CodeMirror handles can be overridden by +// registering a (non-DOM) handler on the editor for the event name, +// and preventDefault-ing the event in that handler. +function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore +} + +function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } +} + +function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 +} + +// Add on and off methods to a constructor's prototype, to make +// registering events on such objects more convenient. +function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; +} + +// Due to the fact that we still support jurassic IE versions, some +// compatibility wrappers are needed. + +function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } +} +function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } +} +function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false +} +function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + +function e_target(e) {return e.target || e.srcElement} +function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b +} + +// Detect drag-and-drop +var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div +}(); + +var zwspSupported; +function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node +} + +// Feature-detect IE's crummy client rect reporting for bidi text +var badBidiRects; +function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) +} + +// See if "".split is the broken IE version, if so, provide an +// alternative way to split lines. +var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result +} : function (string) { return string.split(/\r\n?|\n/); }; + +var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } +} : function (te) { + var range$$1; + try {range$$1 = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range$$1 || range$$1.parentElement() != te) { return false } + return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 +}; + +var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" +})(); + +var badZoomedRects = null; +function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 +} + +// Known modes, by name and by MIME +var modes = {}; +var mimeModes = {}; + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; +} + +function defineMIME(mime, spec) { + mimeModes[mime] = spec; +} + +// Given a MIME type, a {name, ...options} config object, or a name +// string, return a mode config object. +function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } +} + +// Given a mode spec (anything that resolveMode accepts), find and +// initialize an actual mode object. +function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj +} + +// This can be used to attach properties to mode objects from +// outside the actual mode definition. +var modeExtensions = {}; +function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); +} + +function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate +} + +// Given a mode and a state (for that mode), find the inner mode and +// state at the position that the state refers to. +function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} +} + +function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true +} + +// STRING STREAM + +// Fed to the mode parsers, provides helper functions to make +// parsers more succinct. + +var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; +}; + +StringStream.prototype.eol = function () {return this.pos >= this.string.length}; +StringStream.prototype.sol = function () {return this.pos == this.lineStart}; +StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; +StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } +}; +StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} +}; +StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start +}; +StringStream.prototype.eatSpace = function () { + var this$1 = this; + + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } + return this.pos > start +}; +StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; +StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} +}; +StringStream.prototype.backUp = function (n) {this.pos -= n;}; +StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } +}; +StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; +StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } +}; +StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) +}; +StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) +}; + +var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; +}; + +var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; +}; + +Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line +}; + +Context.prototype.baseToken = function (n) { + var this$1 = this; + + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this$1.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} +}; + +Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } +}; + +Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } +}; + +Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state +}; + + +// Compute a style array (an array starting with a mode generation +// -- for invalidation -- followed by pairs of end positions and +// style strings), which is used to highlight the tokens on the +// line. +function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} +} + +function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles +} + +function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context +} + +// Lightweight form of highlight -- proceed over this line and +// update state, but don't save a style array. Used for lines that +// aren't currently visible. +function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } +} + +function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } +} + +function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") +} + +var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; +}; + +// Utility for getTokenAt and getLineTokens +function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) +} + +function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type +} + +// Run the given mode's parser over a line, calling f for each token. +function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } +} + +// Finds the line to start with when starting a parse. Tries to +// find a line with a stateAfter, so that it can start with a +// valid state. If that fails, it returns the line with the +// smallest indentation, which tends to need the least context to +// parse correctly. +function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline +} + +function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); +} + +// LINE DATA STRUCTURE + +// Line objects. These hold state related to a line, including +// highlighting info (the styles array). +var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; +}; + +Line.prototype.lineNo = function () { return lineNo(this) }; +eventMixin(Line); + +// Change the content (text, markers) of a line. Automatically +// invalidates cached information and tries to re-estimate the +// line's height. +function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } +} + +// Detach a line from the document tree and its markers. +function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); +} + +// Convert a style as returned by a mode (either null, or a string +// containing one or more styles) to a CSS style. This is cached, +// and also looks for line-wide styles. +var styleToClassCache = {}; +var styleToClassCacheWithMode = {}; +function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) +} + +// Render the DOM representation of the text of a line. Also builds +// up a 'line map', which points at the DOM nodes that represent +// specific stretches of text, and is used by the measuring code. +// The returned object contains the DOM node, this map, and +// information about line-wide styles that were set by the mode. +function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder +} + +function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token +} + +// Build up the DOM representation for a single token, and add it to +// the line map. Takes care to render special characters separately. +function buildToken(builder, text, style, startStyle, endStyle, title, css) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (title) { token.title = title; } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); +} + +function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result +} + +// Work around nonsense dimensions being reported for stretches of +// right-to-left text. +function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, title, css) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } +} + +function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; +} + +// Outputs a number of spans to make up a line, taking highlighting +// and marked text into account. +function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = title = css = ""; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + if (m.title && !title) { title = m.title; } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } +} + + +// These objects are used to represent the visible (currently drawn) +// part of the document. A LineView may correspond to multiple +// logical lines, if those are connected by collapsed ranges. +function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); +} + +// Create a range of LineView objects for the given lines. +function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array +} + +var operationGroup = null; + +function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } +} + +function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) +} + +function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } +} + +var orphanDelayedCallbacks = null; + +// Often, we want to signal events at a point where we are in the +// middle of some work, but don't want the handler to start calling +// other methods on the editor, which might be in an inconsistent +// state or simply not expect any other events to happen. +// signalLater looks whether there are any handlers, and schedules +// them to be executed when the last operation ends, or, if no +// operation is active, when a timeout fires. +function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); +} + +function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } +} + +// When an aspect of a line changes, a string is added to +// lineView.changes. This updates the relevant part of the line's +// DOM structure. +function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; +} + +// Lines with gutter elements, widgets or a background class need to +// be wrapped, and have the extra elements added to the wrapper div +function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node +} + +function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } +} + +// Wrapper around buildLineContent which will reuse the structure +// in display.externalMeasured when possible. +function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) +} + +// Redraw the line's text. Interacts with the background and text +// classes because the mode may output tokens that influence these +// classes. +function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } +} + +function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; +} + +function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } +} + +function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (node.className == "CodeMirror-linewidget") + { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); +} + +// Build a line's DOM representation from scratch +function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node +} + +// A lineView may contain multiple logical lines (when merged by +// collapsed spans). The widgets for all of them need to be drawn. +function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } +} + +function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } +} + +function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } +} + +function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight +} + +// Return true when the given mouse event happened in a widget +function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } +} + +// POSITION MEASUREMENT + +function paddingTop(display) {return display.lineSpace.offsetTop} +function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} +function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data +} + +function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } +function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth +} +function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight +} + +// Ensure the lineView.wrapping.heights array is populated. This is +// an array of bottom offsets for the lines that make up a drawn +// line. When lineWrapping is on, there might be more than one +// height. +function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } +} + +// Find a line map (mapping character offsets to text nodes) and a +// measurement cache for the given line number. (A line view might +// contain multiple lines when collapsed ranges are present.) +function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } +} + +// Render a line into the hidden node display.externalMeasured. Used +// when measurement is needed for a line that's not in the viewport. +function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view +} + +// Get a {top, bottom, left, right} box (in line-local coordinates) +// for a given character. +function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) +} + +// Find a line view that corresponds to the given line number. +function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } +} + +// Measurement can be split in two steps, the set-up work that +// applies to the whole line, and the measurement of the actual +// character. Functions like coordsChar, that need to do a lot of +// measurements in a row, can thus ensure that the set-up work is +// only done once. +function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } +} + +// Given a prepared measurement object, measures the position of an +// actual character (or fetches it from the cache). +function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} +} + +var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + +function nodeAndOffsetInLineMap(map$$1, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map$$1.length; i += 3) { + mStart = map$$1[i]; + mEnd = map$$1[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map$$1[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { + node = map$$1[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { + node = map$$1[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} +} + +function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect +} + +function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result +} + +// Work around problem with bounding client rects on ranges being +// returned incorrectly when zoomed on IE10 and below. +function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} +} + +function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } +} + +function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } +} + +function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; +} + +function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft +} +function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop +} + +function widgetTopHeight(lineObj) { + var height = 0; + if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) + { height += widgetHeight(lineObj.widgets[i]); } } } + return height +} + +// Converts a {top, bottom, left, right} box from line-local +// coordinates into another coordinate system. Context may be one of +// "line", "div" (display.lineDiv), "local"./null (editor), "window", +// or "page". +function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect +} + +// Coverts a box from "div" coords to another coordinate system. +// Context may be "window", "page", "div", or "local"./null. +function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} +} + +function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) +} + +// Returns a box for a given cursor position, which may have an +// 'other' property containing the position of the secondary cursor +// on a bidi boundary. +// A cursor Pos(line, char, "before") is on the same visual line as `char - 1` +// and after `char - 1` in writing order of `char - 1` +// A cursor Pos(line, char, "after") is on the same visual line as `char` +// and before `char` in writing order of `char` +// Examples (upper-case letters are RTL, lower-case are LTR): +// Pos(0, 1, ...) +// before after +// ab a|b a|b +// aB a|B aB| +// Ab |Ab A|b +// AB B|A B|A +// Every position after the last character on a line is considered to stick +// to the last character on the line. +function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val +} + +// Used to cheaply estimate the coordinates for a position. Used for +// intermediate scroll updates. +function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} +} + +// Positions returned by coordsChar contain some extra information. +// xRel is the relative x position of the input coordinates compared +// to the found position (so xRel > 0 means the coordinates are to +// the right of the character position, for example). When outside +// is true, that means the coordinates lie outside the line's +// vertical range. +function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = true; } + return pos +} + +// Compute the character position closest to the given coordinates. +// Input must be lineSpace-local ("div" coordinate system). +function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var merged = collapsedSpanAtEnd(lineObj); + var mergedPos = merged && merged.find(0, true); + if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) + { lineN = lineNo(lineObj = mergedPos.to.line); } + else + { return found } + } +} + +function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} +} + +function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) +} + +// Returns true if the given side of a box is after the given +// coordinates, in top-to-bottom, left-to-right order. +function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x +} + +function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight$$1 = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight$$1; box.bottom += widgetHeight$$1; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top || y >= coords.bottom; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX) +} + +function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part +} + +function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part +} + +var measureText; +// Compute the default text height. +function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 +} + +// Compute the default character width. +function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 +} + +// Do a bulk-read of the DOM positions and sizes needed to draw the +// view, so that we don't interleave reading and writing to the DOM. +function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; + width[cm.options.gutters[i]] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} +} + +// Computes display.scroller.scrollLeft + display.gutters.offsetWidth, +// but using getBoundingClientRect to get a sub-pixel-accurate +// result. +function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left +} + +// Returns a function that estimates the height of a line, to use as +// first approximation until the line becomes visible (and is thus +// properly measurable). +function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } +} + +function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); +} + +// Given a mouse event, find the corresponding position. If liberal +// is false, it checks whether a gutter or scrollbar was clicked, +// and returns null if it was. forRect is used by rectangular +// selections, and tries to estimate a character position even for +// coordinates beyond the right of the text. +function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords +} + +// Find the view element corresponding to a given line. Return null +// when the line isn't visible. +function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } +} + +function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); +} + +function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range$$1 = doc.sel.ranges[i]; + if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } + var collapsed = range$$1.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + { drawSelectionCursor(cm, range$$1.head, curFragment); } + if (!collapsed) + { drawSelectionRange(cm, range$$1, selFragment); } + } + return result +} + +// Draws a cursor for the given range +function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } +} + +function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + +// Draws the given range as a highlighted selection +function drawSelectionRange(cm, range$$1, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range$$1.from(), sTo = range$$1.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); +} + +// Cursor-blinking +function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, + cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } +} + +function ensureFocus(cm) { + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } +} + +function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + onBlur(cm); + } }, 100); +} + +function onFocus(cm, e) { + if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); +} +function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); +} + +// Read the actual heights of the rendered lines, and update their +// stored heights to match. +function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], height = (void 0); + if (cur.hidden) { continue } + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + } + var diff = cur.line.height - height; + if (height < 2) { height = textHeight(display); } + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + } +} + +// Read and store the height of line widgets associated with the +// given line. +function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } +} + +// Compute the lines that are visible in a given viewport (defaults +// the the current scroll position). viewport may contain top, +// height, and ensure (see op.scrollToPos) properties. +function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} +} + +// Re-align line numbers and gutter marks to compensate for +// horizontal scrolling. +function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } +} + +// Used to ensure that the line number gutter is still the right +// size for the current document size. Returns true when an update +// is needed. +function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm); + return true + } + return false +} + +// SCROLLING THINGS INTO VIEW + +// If an editor sits on the top or bottom of the window, partially +// scrolled out of view, this ensures that the cursor is visible. +function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } +} + +// Scroll a given position into view (immediately), verifying that +// it actually became visible (as line heights are accurately +// measured, the position of something may 'drift' during drawing). +function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect +} + +// Scroll a given set of coordinates into view (immediately). +function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } +} + +// Calculate a new scroll position needed to scroll the given +// rectangle into view. Returns an object with scrollTop and +// scrollLeft properties. When these are undefined, the +// vertical/horizontal position does not need to be adjusted. +function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; + var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result +} + +// Store a relative adjustment to the scroll position in the current +// operation (to be applied when the operation finishes). +function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; +} + +// Make sure that at the end of the operation the current cursor is +// shown. +function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; +} + +function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } +} + +function scrollToRange(cm, range$$1) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range$$1; +} + +// When an operation has its scrollToPos property set, and another +// scroll action is applied before the end of the operation, this +// 'simulates' scrolling that position into view in a cheap way, so +// that the effect of intermediate scroll commands is not ignored. +function resolveScrollToPos(cm) { + var range$$1 = cm.curOp.scrollToPos; + if (range$$1) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); + scrollToCoordsRange(cm, from, to, range$$1.margin); + } +} + +function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); +} + +// Sync the scrollable area and scrollbars, ensure the viewport +// covers the visible area. +function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); +} + +function setScrollTop(cm, val, forceScroll) { + val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } +} + +// Sync scroller and scrollbar, ensure the gutter elements are +// aligned. +function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); +} + +// SCROLLBARS + +// Prepare DOM reads needed to update the scrollbars. Done in one +// shot to minimize update/measure roundtrips. +function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } +} + +var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } +}; + +NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} +}; + +NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } +}; + +NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } +}; + +NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; +}; + +NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); +}; + +NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); +}; + +var NullScrollbars = function () {}; + +NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; +NullScrollbars.prototype.setScrollLeft = function () {}; +NullScrollbars.prototype.setScrollTop = function () {}; +NullScrollbars.prototype.clear = function () {}; + +function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } +} + +// Re-synchronize the fake scrollbars with the actual size of the +// content. +function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } +} + +var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + +function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } +} + +// Operations are used to wrap a series of changes to the editor +// state in such a way that each change won't have to update the +// cursor and display (which would be awkward, slow, and +// error-prone). Instead, display updates are batched and then all +// combined and executed at once. + +var nextOpId = 0; +// Start a new operation. +function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: null, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId // Unique ID + }; + pushOperation(cm.curOp); +} + +// Finish an operation, updating the display and signalling delayed events +function endOperation(cm) { + var op = cm.curOp; + finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); +} + +// The DOM updates done when an operation finishes are batched so +// that the minimum number of relayouts are required. +function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } +} + +function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); +} + +function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); +} + +function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } +} + +function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } +} + +function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } +} + +// Run the given function in an operation +function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } +} +// Wraps a function in an operation. Returns the wrapped function. +function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } +} +// Used to add methods to editor and doc instances, wrapping them in +// operations. +function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } +} +function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } +} + +// Updates the display.view data structure for a given change to the +// document. From and to are in pre-change coordinates. Lendiff is +// the amount of lines added or subtracted by the change. This is +// used for changes that span multiple lines, or change the way +// lines are divided into visual lines. regLineChange (below) +// registers single-line changes. +function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } +} + +// Register a change to a single line. Type must be one of "text", +// "gutter", "class", "widget" +function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } +} + +// Clear the view. +function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; +} + +function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} +} + +// Force the view to cover a given range, adding empty view element +// or clipping off existing ones as needed. +function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; +} + +// Count the number of lines in the view whose DOM representation is +// out of date (or nonexistent). +function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty +} + +// HIGHLIGHT WORKER + +function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } +} + +function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } +} + +// DISPLAY DRAWING + +var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; +}; + +DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } +}; +DisplayUpdate.prototype.finish = function () { + var this$1 = this; + + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this$1.events[i]); } +}; + +function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } +} + +function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result +} + +function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range$$1 = document.createRange(); + range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range$$1.collapse(false); + sel.removeAllRanges(); + sel.addRange(range$$1); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } +} + +// Does the actual updating of the line display. Bails out +// (returning false) when there is nothing to be done and forced is +// false. +function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true +} + +function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } +} + +function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } +} + +// Sync the actual display DOM structure with display.view, removing +// nodes for lines that are no longer in view, and creating the ones +// that are not there yet, and updating the ones that are out of +// date. +function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) { + } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } +} + +function updateGutterSpace(cm) { + var width = cm.display.gutters.offsetWidth; + cm.display.sizer.style.marginLeft = width + "px"; +} + +function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; +} + +// Rebuild the gutter elements, ensure the margin to the left of the +// code matches their width. +function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + var i = 0; + for (; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = i ? "" : "none"; + updateGutterSpace(cm); +} + +// Make sure the gutters options contains the element +// "CodeMirror-linenumbers" when the lineNumbers option is true. +function setGuttersForLineNumbers(options) { + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(found, 1); + } +} + +// Since the delta values reported on mouse wheel events are +// unstandardized between browsers and even browser versions, and +// generally horribly unpredictable, this code starts by measuring +// the scroll effect that the first few mouse wheel events have, +// and, from that, detects the way it can convert deltas to pixel +// offsets afterwards. +// +// The reason we want to know the amount a wheel event will scroll +// is that it gives us a chance to update the display before the +// actual scrolling happens, reducing flickering. + +var wheelSamples = 0; +var wheelPixelsPerUnit = null; +// Fill in a browser-detected starting value on browsers where we +// know one. These don't have to be accurate -- the result of them +// being wrong would just be a slight flicker on the first wheel +// scroll (if it is large enough). +if (ie) { wheelPixelsPerUnit = -.53; } +else if (gecko) { wheelPixelsPerUnit = 15; } +else if (chrome) { wheelPixelsPerUnit = -.7; } +else if (safari) { wheelPixelsPerUnit = -1/3; } + +function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} +} +function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta +} + +function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } +} + +// Selection objects are immutable. A new one is created every time +// the selection changes. A selection is one or more non-overlapping +// (and non-touching) ranges, sorted, and an integer that indicates +// which one is the primary selection (the one that's scrolled into +// view, that getCursor returns, etc). +var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; +}; + +Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + +Selection.prototype.equals = function (other) { + var this$1 = this; + + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this$1.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true +}; + +Selection.prototype.deepCopy = function () { + var this$1 = this; + + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } + return new Selection(out, this.primIndex) +}; + +Selection.prototype.somethingSelected = function () { + var this$1 = this; + + for (var i = 0; i < this.ranges.length; i++) + { if (!this$1.ranges[i].empty()) { return true } } + return false +}; + +Selection.prototype.contains = function (pos, end) { + var this$1 = this; + + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this$1.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 +}; + +var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; +}; + +Range.prototype.from = function () { return minPos(this.anchor, this.head) }; +Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; +Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + +// Take an unsorted, potentially overlapping set of ranges, and +// build a selection out of it. 'Consumes' ranges array (modifying +// it). +function normalizeSelection(ranges, primIndex) { + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + if (cmp(prev.to(), cur.from()) >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) +} + +function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) +} + +// Compute the position of the end of a change (its 'to' property +// refers to the pre-change end). +function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) +} + +// Adjust a position to refer to the post-change position of the +// same text, or the end of the change if the change covers it. +function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) +} + +function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(out, doc.sel.primIndex) +} + +function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } +} + +// Used by replaceSelections to allow moving the selection to the +// start or around the replaced test. Hint may be "start" or "around". +function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) +} + +// Used to get the editor into a consistent state again when options change. + +function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); +} + +function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } +} + +// DOCUMENT DATA STRUCTURE + +// By default, updates that start and end at the beginning of a line +// are treated specially, in order to make the association of line +// widgets and marker elements with the text behave more intuitive. +function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) +} + +// Perform a change on the document data structure. +function updateDoc(doc, change, markedSpans, estimateHeight$$1) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight$$1); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); +} + +// Call f for all linked documents. +function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); +} + +// Attach a document to an editor. +function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); +} + +function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); +} + +function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); +} + +function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; +} + +// Create a history change event from an updateDoc-style change +// object. +function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange +} + +// Pop all selection events off the end of a history array. Stop at +// a change event. +function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } +} + +// Find the top change event in the history. Pop off selection +// events that are in the way. +function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } +} + +// Register a change in the history. Merges changes that are within +// a single operation, or are close together with an origin that +// allows merging (starting with "+") into a single event. +function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } +} + +function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) +} + +// Called whenever the selection changes, sets the new selection as +// the pending selection in the history, and pushes the old pending +// selection into the 'done' array when it was significantly +// different (in number of selected ranges, emptiness, or time). +function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } +} + +function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } +} + +// Used to store marked span information in the history. +function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); +} + +// When un/re-doing restores text containing marked spans, those +// that have been explicitly cleared should not be restored. +function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null +} + +// Retrieve and filter the old marked spans stored in a change event. +function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw +} + +// Used for un/re-doing changes from the history. Combines the +// result of computing the existing spans with the set of spans that +// existed in the history (so that deleting around a span and then +// undoing brings back the span). +function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old +} + +// Used both to provide a JSON-safe object in .getHistory, and, when +// detaching a document, to split the history in two +function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy +} + +// The 'scroll' parameter given to many of these indicated whether +// the new cursor position should be scrolled into view after +// modifying the selection. + +// If shift is held or the extend flag is set, extends a range to +// include a given position (and optionally a second position). +// Otherwise, simply returns the range between the given positions. +// Used for cursor motion and such. +function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } +} + +// Extend the primary selection range, discard the rest. +function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); +} + +// Extend all selections (pos is an array of selections with length +// equal the number of selections) +function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(out, doc.sel.primIndex); + setSelection(doc, newSel, options); +} + +// Updates a single range in the selection. +function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); +} + +// Reset the selection to a single range. +function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); +} + +// Give beforeSelectionChange handlers a change to influence a +// selection update. +function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + var this$1 = this; + + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) } + else { return sel } +} + +function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } +} + +// Set a new selection. +function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); +} + +function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + { ensureCursorVisible(doc.cm); } +} + +function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); +} + +// Verify that the selection does not partially select any atomic +// marked ranges. +function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); +} + +// Return a selection that does not partially select any atomic +// ranges. +function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(out, sel.primIndex) : sel +} + +function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos +} + +// Ensure a given position is not inside an atomic range. +function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found +} + +function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } +} + +function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); +} + +// UPDATING + +// Allow "beforeChange" event handlers to influence a change +function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { return null } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} +} + +// Apply a change to a document, and add it to the document's +// history, and propagating it to all linked documents. +function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } +} + +function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); +} + +// Revert a change stored in a document's history. +function makeChangeFromHistory(doc, type, allowSelectionOnly) { + if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } + else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } +} + +// Sub-views need their line numbers shifted when text is added +// above or below them in the parent document. +function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } +} + +// More lower-level change function, handling only a single document +// (not linked ones). +function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); +} + +// Handle the interaction of a change to a document with the editor +// that this document is part of. +function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; +} + +function replaceRange(doc, code, from, to, origin) { + if (!to) { to = from; } + if (cmp(to, from) < 0) { var assign; + (assign = [to, from], from = assign[0], to = assign[1], assign); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); +} + +// Rebasing/resetting history to deal with externally-sourced changes + +function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } +} + +// Tries to rebase an array of history events given a change in the +// document. If the change touches the same lines as the event, the +// event, and everything 'behind' it, is discarded. If the change is +// before the event, the event's positions are updated. Uses a +// copy-on-write scheme for the positions, to avoid having to +// reallocate them all on every rebase, but also avoid problems with +// shared position objects being unsafely updated. +function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } +} + +function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); +} + +// Utility for applying a change to a line by handle or number, +// returning the number and optionally registering the line as +// changed. +function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line +} + +// The document is represented as a BTree consisting of leaves, with +// chunk of lines in them, and branches, with up to ten leaves or +// other branch nodes below them. The top node is always a branch +// node, and is the document object itself (meaning it has +// additional methods and properties). +// +// All nodes have parent links. The tree is used both to go from +// line numbers to line objects, and to go from objects to numbers. +// It also indexes by height, and is used to convert between height +// and line object, and to find the total height of the document. +// +// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + +function LeafChunk(lines) { + var this$1 = this; + + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this$1; + height += lines[i].height; + } + this.height = height; +} + +LeafChunk.prototype = { + chunkSize: function chunkSize() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function removeInner(at, n) { + var this$1 = this; + + for (var i = at, e = at + n; i < e; ++i) { + var line = this$1.lines[i]; + this$1.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function collapse(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function insertInner(at, lines, height) { + var this$1 = this; + + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } + }, + + // Used to iterate over a part of the tree. + iterN: function iterN(at, n, op) { + var this$1 = this; + + for (var e = at + n; at < e; ++at) + { if (op(this$1.lines[at])) { return true } } + } +}; + +function BranchChunk(children) { + var this$1 = this; + + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this$1; + } + this.size = size; + this.height = height; + this.parent = null; +} + +BranchChunk.prototype = { + chunkSize: function chunkSize() { return this.size }, + + removeInner: function removeInner(at, n) { + var this$1 = this; + + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this$1.height -= oldHeight - child.height; + if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function collapse(lines) { + var this$1 = this; + + for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } + }, + + insertInner: function insertInner(at, lines, height) { + var this$1 = this; + + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this$1.children.splice(++i, 0, leaf); + leaf.parent = this$1; + } + child.lines = child.lines.slice(0, remaining); + this$1.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function maybeSpill() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function iterN(at, n, op) { + var this$1 = this; + + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } +}; + +// Line widgets are block elements displayed above or below a line. + +var LineWidget = function(doc, node, options) { + var this$1 = this; + + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this$1[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; +}; + +LineWidget.prototype.clear = function () { + var this$1 = this; + + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } +}; + +LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + updateLineHeight(line, line.height + diff); + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } +}; +eventMixin(LineWidget); + +function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } +} + +function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); + return widget +} + +// TEXTMARKERS + +// Created with markText and setBookmark methods. A TextMarker is a +// handle that can be used to clear or find a marked position in the +// document. Line objects hold arrays (markedSpans) containing +// {from, to, marker} object pointing to such marker objects, and +// indicating that such a marker is present on that line. Multiple +// lines may point to the same marker when it spans across lines. +// The spans will have null for their from/to properties when the +// marker continues beyond the start/end of the line. Markers have +// links back to the lines they currently touch. + +// Collapsed markers have unique ids, in order to be able to order +// them, which is needed for uniquely determining an outer marker +// when they overlap (they may nest, but not partially overlap). +var nextMarkerId = 0; + +var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; +}; + +// Clear the marker. +TextMarker.prototype.clear = function () { + var this$1 = this; + + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this$1.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this$1); + if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } +}; + +// Find the position of the marker in the document. Returns a {from, +// to} object by default. Side can be passed to get a specific side +// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the +// Pos objects returned contain a line object, rather than a line +// number (used to prevent looking up the same line twice). +TextMarker.prototype.find = function (side, lineObj) { + var this$1 = this; + + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this$1.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this$1); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} +}; + +// Signals that the marker's widget changed, and surrounding layout +// should be recomputed. +TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); +}; + +TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); +}; + +TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } +}; +eventMixin(TextMarker); + +// Create a marker, wire it up to the right lines, and +function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker +} + +// SHARED TEXTMARKERS + +// A shared marker spans multiple linked documents. It is +// implemented as a meta-marker-object controlling multiple normal +// markers. +var SharedTextMarker = function(markers, primary) { + var this$1 = this; + + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this$1; } +}; + +SharedTextMarker.prototype.clear = function () { + var this$1 = this; + + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this$1.markers[i].clear(); } + signalLater(this, "clear"); +}; + +SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) +}; +eventMixin(SharedTextMarker); + +function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) +} + +function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) +} + +function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } +} + +function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); +} + +var nextDocId = 0; +var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); +}; + +Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range$$1 = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range$$1.head; } + else if (start == "anchor") { pos = range$$1.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } + else { pos = range$$1.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + var this$1 = this; + + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this$1, ranges[i].anchor), + clipPos(this$1, ranges[i].head)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var this$1 = this; + + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var this$1 = this; + + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var this$1 = this; + + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range$$1 = sel.ranges[i]; + changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this$1, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo$$1 = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || + span.from == null && lineNo$$1 != from.line || + span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo$$1; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo$$1; + }); + return clipPos(this, Pos(lineNo$$1, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + var this$1 = this; + + if (other instanceof CodeMirror$1) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this$1.linked[i]; + if (link.doc != other) { continue } + this$1.linked.splice(i, 1); + other.unlinkDoc(this$1); + detachSharedMarkers(findSharedMarkers(this$1)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) +}); + +// Public alias. +Doc.prototype.eachLine = Doc.prototype.iter; + +// Kludge to work around strange IE behavior where it'll sometimes +// re-fire a series of drag-related events right after the drop (#1551) +var lastDrop = 0; + +function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) + { return } + + var reader = new FileReader; + reader.onload = operation(cm, function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } + text[i] = content; + if (++read == n) { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + } + }); + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) { loadFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e){} + } +} + +function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } +} + +function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); +} + +function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } +} + +// These must be handled carefully, because naively registering a +// handler for each editor will cause the editors to never be +// garbage collected. + +function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"); + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { f(cm); } + } +} + +var globalsRegistered = false; +function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; +} +function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); +} +// Called when the window resizes +function onResize(cm) { + var d = cm.display; + if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) + { return } + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); +} + +var keyNames = { + 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" +}; + +// Number keys +for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } +// Alphabetic keys +for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } +// Function keys +for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + +var keyMap = {}; + +keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" +}; +// Note that the save and find-related commands aren't defined by +// default. User code or addons can define them. Unknown commands +// are simply ignored. +keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + fallthrough: "basic" +}; +// Very basic readline/emacs-style bindings, which are standard on Mac. +keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" +}; +keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + fallthrough: ["basic", "emacsy"] +}; +keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + +// KEYMAP DISPATCH + +function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name +} + +// This is a kludge to keep keymaps mostly working as raw objects +// (backwards compatibility) while at the same time support features +// like normalization and multi-stroke key bindings. It compiles a +// new normalized keymap, and then updates the old object to reflect +// this. +function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap +} + +function lookupKey(key, map$$1, handle, context) { + map$$1 = getKeyMap(map$$1); + var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map$$1.fallthrough) { + if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") + { return lookupKey(key, map$$1.fallthrough, handle, context) } + for (var i = 0; i < map$$1.fallthrough.length; i++) { + var result = lookupKey(key, map$$1.fallthrough[i], handle, context); + if (result) { return result } + } + } +} + +// Modifier key presses don't count as 'real' key presses for the +// purpose of keymap fallthrough. +function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" +} + +function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name +} + +// Look up the name of a key as indicated by an event object. +function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + return addModifierNames(name, event, noShift) +} + +function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val +} + +// Helper for deleting text near the selection(s), used to implement +// backspace, delete, and similar functionality. +function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); +} + +function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target +} + +function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") +} + +function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") +} + +function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null +} + +// Commands are parameter-less actions that can be performed on an +// editor, mostly used for keybindings. +var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } +}; + + +function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) +} +function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) +} +function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start +} + +// Run a handler that was bound to a key. +function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done +} + +function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) +} + +// Note that, despite the name, this function is also used to check +// for bound mouse clicks. + +var stopSeq = new Delayed; + +function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) +} + +function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result +} + +// Handle a key from the keydown event. +function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } +} + +// Handle a key from the keypress event +function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) +} + +var lastStoppedKey = null; +function onKeyDown(e) { + var cm = this; + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } +} + +function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); +} + +function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); +} + +function onKeyPress(e) { + var cm = this; + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); +} + +var DOUBLECLICK_DELAY = 400; + +var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; +}; + +PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button +}; + +var lastClick; +var lastDoubleClick; +function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } +} + +// A mouse down can be a single click, double click, triple click, +// start of selection drag, start of text drag, new cursor +// (ctrl-click), rectangle drag (alt-drag), or xwin +// middle-click-paste. Or it might be a click on something we should +// not interfere with, such as a scrollbar or widget. +function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { onContextMenu(cm, e); } + else { delayBlurEvent(cm); } + } +} + +function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) +} + +function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value +} + +function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } +} + +// Start a text drag. When it ends, see if any dragging actually +// happen, and treat as a click if it didn't. +function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + off(document, "mouseup", dragEnd); + off(document, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if (webkit || ie && ie_version == 9) + { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + on(document, "mouseup", dragEnd); + on(document, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + delayBlurEvent(cm); + setTimeout(function () { return display.input.focus(); }, 20); +} + +function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) +} + +// Normal selection, as opposed to text dragging. +function leftButtonSelect(cm, event, start, behavior) { + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range$$1 = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } + else + { ourRange = range$$1; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range$$1 = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range$$1.anchor, anchor) > 0) { + head = range$$1.head; + anchor = minPos(oldRange.from(), range$$1.anchor); + } else { + head = range$$1.anchor; + anchor = maxPos(oldRange.to(), range$$1.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); + setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + e_preventDefault(e); + display.input.focus(); + off(document, "mousemove", move); + off(document, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (!e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(document, "mousemove", move); + on(document, "mouseup", up); +} + +// Used when mouse-selecting to adjust the anchor to the proper side +// of a bidi jump depending on the visual position of the head. +function bidiSimplify(cm, range$$1) { + var anchor = range$$1.anchor; + var head = range$$1.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 } + var order = getOrder(anchorLine); + if (!order) { return range$$1 } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range$$1 } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head) +} + + +// Determines whether an event happened in the gutter, and fires the +// handlers for the corresponding event. +function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signal(cm, type, cm, line, gutter, e); + return e_defaultPrevented(e) + } + } +} + +function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) +} + +// CONTEXT MENU HANDLING + +// To make the context menu work, we need to briefly unhide the +// textarea (making it as unobtrusive as possible) to let the +// right-click take effect on it. +function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + cm.display.input.onContextMenu(e); +} + +function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) +} + +function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); +} + +var Init = {toString: function(){return "CodeMirror.Init"}}; + +var defaults = {}; +var optionHandlers = {}; + +function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + guttersChanged(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("firstLineNumber", 1, guttersChanged, true); + option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); +} + +function guttersChanged(cm) { + updateGutters(cm); + regChange(cm); + alignHorizontally(cm); +} + +function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } +} + +function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); +} + +// A CodeMirror instance represents an editor. This is the object +// that user code is usually dealing with. + +function CodeMirror$1(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + setGuttersForLineNumbers(options); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + this.doc = doc; + + var input = new CodeMirror$1.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input); + display.wrapper.CodeMirror = this; + updateGutters(this); + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(bind(onFocus, this), 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this$1, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } +} + +// The default configuration options. +CodeMirror$1.defaults = defaults; +// Functions to run when options are changed. +CodeMirror$1.optionHandlers = optionHandlers; + +// Attach the necessary event handlers when initializing the editor +function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); } + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); +} + +var initHooks = []; +CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }; + +// Indent the given line. The how parameter can be "smart", +// "add"/null, "subtract", or "prev". When aggressive is false +// (typically set to true for forced single-line indents), empty +// lines are not indented, and places where the mode returns Pass +// are left alone. +function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } +} + +// This will be set to a {lineWise: bool, text: [string]} object, so +// that, when pasting, we know what kind of selections the copied +// text was made out of. +var lastCopied = null; + +function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; +} + +function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var paste = cm.state.pasteIncoming || origin == "paste"; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasing N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range$$1 = sel.ranges[i$1]; + var from = range$$1.from(), to = range$$1.to(); + if (range$$1.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) + { from = to = Pos(from.line, 0); } + } + updateInput = cm.curOp.updateInput; + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + cm.curOp.updateInput = updateInput; + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = false; +} + +function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } +} + +function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range$$1 = sel.ranges[i]; + if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } + var mode = cm.getModeAt(range$$1.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range$$1.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) + { indented = indentLine(cm, range$$1.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } + } +} + +function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} +} + +function disableBrowserMagic(field, spellcheck) { + field.setAttribute("autocorrect", "off"); + field.setAttribute("autocapitalize", "off"); + field.setAttribute("spellcheck", !!spellcheck); +} + +function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div +} + +// The publicly visible API. Note that methodOp(f) means +// 'wrap f in an operation, performed on its `this` parameter'. + +// This is not the complete set of editor methods. Most of the +// methods defined on the Doc type are also injected into +// CodeMirror.prototype, for backwards compatibility and +// convenience. + +var addEditorMethods = function(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map$$1, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); + }, + removeKeyMap: function(map$$1) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map$$1 || maps[i].name == map$$1) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var this$1 = this; + + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this$1.state.modeGen++; + regChange(this$1); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var this$1 = this; + + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range$$1 = ranges[i]; + if (!range$$1.empty()) { + var from = range$$1.from(), to = range$$1.to(); + var start = Math.max(end, from.line); + end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this$1, j, how); } + var newRanges = this$1.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range$$1.head.line > end) { + indentLine(this$1, range$$1.head.line, how, true); + end = range$$1.head.line; + if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var this$1 = this; + + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range$$1 = this.doc.sel.primary(); + if (start == null) { pos = range$$1.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range$$1.from() : range$$1.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var this$1 = this; + + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this$1.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range$$1) { + if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) + { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range$$1.from() : range$$1.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range$$1) { + var other = findPosH(doc, range$$1.head, dir, unit, false); + return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var this$1 = this; + + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this$1, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this$1, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range$$1) { + if (collapse) + { return dir < 0 ? range$$1.from() : range$$1.to() } + var headPos = cursorCoords(this$1, range$$1.head, "div"); + if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range$$1 == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range$$1, margin) { + if (range$$1 == null) { + range$$1 = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range$$1 == "number") { + range$$1 = {from: Pos(range$$1, 0), to: null}; + } else if (range$$1.from == null) { + range$$1 = {from: range$$1, to: null}; + } + if (!range$$1.to) { range$$1.to = range$$1.from; } + range$$1.margin = margin || 0; + + if (range$$1.from.line != null) { + scrollToRange(this, range$$1); + } else { + scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo$$1 = this.display.viewFrom; + this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } + ++lineNo$$1; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; +}; + +// Used for horizontal relative motion. Dir is -1 or 1 (left or +// right), unit can be "char", "column" (like char, but doesn't +// cross line boundaries), "word" (across next word), or "group" (to +// the start of next group of word or non-word-non-whitespace +// chars). The visually param controls whether, in right-to-left +// text, direction 1 means to move towards the next index in the +// string, or towards the character to the right of the current +// position. The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + function findNextLine() { + var l = pos.line + dir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result +} + +// For relative vertical movement. Dir may be -1 or 1. Unit can be +// "page" or "line". The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target +} + +// CONTENTEDITABLE INPUT STYLE + +var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; +}; + +ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + disableBrowserMagic(div, cm.options.spellcheck); + + on(div, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); +}; + +ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = this.cm.state.focused; + return result +}; + +ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); +}; + +ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); +}; + +ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); +}; + +ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); +}; + +ContentEditableInput.prototype.rememberSelection = function () { + var sel = window.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; +}; + +ContentEditableInput.prototype.selectionInEditor = function () { + var sel = window.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) +}; + +ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor()) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } +}; +ContentEditableInput.prototype.blur = function () { this.div.blur(); }; +ContentEditableInput.prototype.getField = function () { return this.div }; + +ContentEditableInput.prototype.supportsTouch = function () { return true }; + +ContentEditableInput.prototype.receivedFocus = function () { + var input = this; + if (this.selectionInEditor()) + { this.pollSelection(); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); +}; + +ContentEditableInput.prototype.selectionChanged = function () { + var sel = window.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset +}; + +ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = window.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } +}; + +ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } +}; + +ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); +}; +ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); +}; +ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); +}; +ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); +}; + +ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } +}; + +ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; +}; + +ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } +}; + +ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); +}; + +ContentEditableInput.prototype.onContextMenu = function () {}; +ContentEditableInput.prototype.resetPosition = function () {}; + +ContentEditableInput.prototype.needsContentAttribute = true; + +function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result +} + +function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false +} + +function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + +function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(); + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + closing = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText != null) { + addText(cmText || node.textContent.replace(/\u200b/g, "")); + return + } + var markerID = node.getAttribute("cm-marker"), range$$1; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range$$1 = found[0].find(0))) + { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p)$/i.test(node.nodeName); + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + } + return text +} + +function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } +} + +function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map$$1 = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map$$1.length; j += 3) { + var curNode = map$$1[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map$$1[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } +} + +// TEXTAREA INPUT STYLE + +var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; +}; + +TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + + // Wraps and hides input textarea + var div = this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + var te = this.textarea = div.firstChild; + display.wrapper.insertBefore(div, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = true; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = true; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + cm.state.pasteIncoming = true; + input.focus(); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); +}; + +TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result +}; + +TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } +}; + +// Reset the input to correspond to the selection (or to be empty, +// when not typing and nothing is selected) +TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var cm = this.cm; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } +}; + +TextareaInput.prototype.getField = function () { return this.textarea }; + +TextareaInput.prototype.supportsTouch = function () { return false }; + +TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } +}; + +TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + +TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; +}; + +TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + +// Poll for input changes, using the normal rate of polling. This +// runs as long as the editor is focused. +TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); +}; + +// When an event has just come in that is likely to add or change +// something in the input textarea, we poll faster, to ensure that +// the change appears on the screen quickly. +TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); +}; + +// Read input from the textarea, and update the document to match. +// When something is selected, it is present in the textarea, and +// selected (unless it is huge, in which case a placeholder is +// used). When nothing is selected, the cursor sits after previously +// seen text (can be empty), which is stored in prevInput (we must +// not reset the textarea when typing, because that breaks IME). +TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true +}; + +TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } +}; + +TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); +}; + +TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + input.wrapper.style.cssText = "position: absolute"; + var wrapperBox = input.wrapper.getBoundingClientRect(); + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = true; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } +}; + +TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; +}; + +TextareaInput.prototype.setUneditable = function () {}; + +TextareaInput.prototype.needsContentAttribute = false; + +function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm +} + +function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; +} + +// EDITOR CONSTRUCTOR + +defineOptions(CodeMirror$1); + +addEditorMethods(CodeMirror$1); + +// Set up methods on CodeMirror's prototype to redirect to the editor's document. +var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); +for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror$1.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + +eventMixin(Doc); + +// INPUT HANDLING + +CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + +// MODE DEFINITION AND QUERYING + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +CodeMirror$1.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; } + defineMode.apply(this, arguments); +}; + +CodeMirror$1.defineMIME = defineMIME; + +// Minimal default mode. +CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); +CodeMirror$1.defineMIME("text/plain", "null"); + +// EXTENSIONS + +CodeMirror$1.defineExtension = function (name, func) { + CodeMirror$1.prototype[name] = func; +}; +CodeMirror$1.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; +}; + +CodeMirror$1.fromTextArea = fromTextArea; + +addLegacyProps(CodeMirror$1); + +CodeMirror$1.version = "5.32.0"; + +return CodeMirror$1; + +}))); From f428c72bdbeaa97386c2d54b78b00ba8ec59cdb5 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 20:05:19 -0500 Subject: [PATCH 220/414] Remove sequencer feature, too similar to the mcopy feature and not needed in a standalone intervalometer. --- app/www/index.html | 8 ++------ app/www/static/css/index.css | 17 +++++++++++------ app/www/static/js/intval.core.js | 5 ----- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 5088dec..7a63684 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -74,12 +74,11 @@
    -
    -

    SEQUENCER

    -

    MSCRIPT

    + +
    @@ -88,9 +87,6 @@
    -
    -
    -
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 0e9b9a1..730c2c5 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -115,7 +115,7 @@ input[type=text], input[type=password], select{ width: 100%; - border: 2px solid #fff; + border: 1px solid #fff; border-radius: 5px; color: #fff; font-size: 18px; @@ -132,7 +132,7 @@ option{ } button{ width: 100%; - border: 2px solid #fff; + border: 1px solid #fff; border-radius: 5px; color: #fff; font-size: 18px; @@ -199,9 +199,6 @@ button:focus{ #appIcon > div{ background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDYwIDYwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2MCA2MDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPgo8Zz4KCTxwYXRoIGQ9Ik00NS41NjMsMjkuMTc0bC0yMi0xNWMtMC4zMDctMC4yMDgtMC43MDMtMC4yMzEtMS4wMzEtMC4wNThDMjIuMjA1LDE0LjI4OSwyMiwxNC42MjksMjIsMTV2MzAgICBjMCwwLjM3MSwwLjIwNSwwLjcxMSwwLjUzMywwLjg4NEMyMi42NzksNDUuOTYyLDIyLjg0LDQ2LDIzLDQ2YzAuMTk3LDAsMC4zOTQtMC4wNTksMC41NjMtMC4xNzRsMjItMTUgICBDNDUuODM2LDMwLjY0LDQ2LDMwLjMzMSw0NiwzMFM0NS44MzYsMjkuMzYsNDUuNTYzLDI5LjE3NHogTTI0LDQzLjEwN1YxNi44OTNMNDMuMjI1LDMwTDI0LDQzLjEwN3oiIGZpbGw9IiNGRkZGRkYiLz4KCTxwYXRoIGQ9Ik0zMCwwQzEzLjQ1OCwwLDAsMTMuNDU4LDAsMzBzMTMuNDU4LDMwLDMwLDMwczMwLTEzLjQ1OCwzMC0zMFM0Ni41NDIsMCwzMCwweiBNMzAsNThDMTQuNTYxLDU4LDIsNDUuNDM5LDIsMzAgICBTMTQuNTYxLDIsMzAsMnMyOCwxMi41NjEsMjgsMjhTNDUuNDM5LDU4LDMwLDU4eiIgZmlsbD0iI0ZGRkZGRiIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat; } -#sequencerIcon > div{ - background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAyOTAgMjkwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyOTAgMjkwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjMycHgiIGhlaWdodD0iMzJweCI+CjxnPgoJPHJlY3QgeT0iMjIwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHk9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgeT0iMjIwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgeT0iMTEwIiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIGZpbGw9IiNGRkZGRkYiLz4KCTxyZWN0IHg9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHk9IjIyMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHk9IjExMCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiBmaWxsPSIjRkZGRkZGIi8+Cgk8cmVjdCB4PSIyMjAiIHdpZHRoPSI3MCIgaGVpZ2h0PSI3MCIgZmlsbD0iI0ZGRkZGRiIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat; -} footer{ width: 100%; @@ -212,13 +209,17 @@ footer{ } footer .icon { - width: 25%; + width: 33.33%; height: 50px; float: left; box-sizing: border-box; border-right: 1px solid rgba(255, 255, 255, 0.3); } +footer .icon:last-child{ + border-right: 0; +} + .icon > div{ display: block; line-height: 34px; @@ -239,3 +240,7 @@ footer > div.selected{ .CodeMirror{ font-size: 18px; } + +#compile{ + margin-top: 20px; +} diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 3fdc059..c6335bc 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -148,11 +148,6 @@ var settingsPage = function () { document.getElementById('settings').classList.add('selected'); document.getElementById('settingsIcon').classList.add('selected'); }; -var sequencerPage = function () { - unsetPages(); - document.getElementById('sequencer').classList.add('selected'); - document.getElementById('sequencerIcon').classList.add('selected'); -}; var mscriptPage = function () { unsetPages(); document.getElementById('mscript').classList.add('selected'); From a79d13d382a39f248e13321b019306781c0ff532 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 20:26:10 -0500 Subject: [PATCH 221/414] Add sequence module and db module placeholder --- lib/db/index.js | 2 ++ lib/sequence/index.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 lib/db/index.js create mode 100644 lib/sequence/index.js diff --git a/lib/db/index.js b/lib/db/index.js new file mode 100644 index 0000000..b7ff1bc --- /dev/null +++ b/lib/db/index.js @@ -0,0 +1,2 @@ +'use strict' + diff --git a/lib/sequence/index.js b/lib/sequence/index.js new file mode 100644 index 0000000..98d371a --- /dev/null +++ b/lib/sequence/index.js @@ -0,0 +1,17 @@ +'use strict' + +const sequence = { + active : false, + frame: false, + delay : false +} + +sequence.start = function (options, cb) { + sequence.active = true +} + +sequence.stop = function (cb) { + sequence.active = false +} + +module.exports = sequence \ No newline at end of file From 9c5d4290514e92799327494dbea17feb4b16aa2a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 22 Nov 2017 20:26:34 -0500 Subject: [PATCH 222/414] Use sequence module and begin fashioning requests around it. --- index.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 175a589..06395b8 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ const fs = require('fs') const ble = require('./lib/ble') const intval = require('./lib/intval') +const sequence = require('./lib/sequence') const PACKAGE = require('./package.json') const PORT = process.env.PORT || 6699 @@ -29,8 +30,8 @@ function createServer () { app.post('/counter', rCounter) app.get( '/frame', rFrame) app.post('/frame', rFrame) - app.get( '/sequence', () => {}) - app.post('/sequence', () => {}) + app.get( '/sequence', rSequence) + app.post('/sequence', rSequence) app.post('/reset', rReset) app.get( '/status', rStatus) app.listen(PORT, () => { @@ -111,7 +112,8 @@ function rDelay (req, res, next) { delay = req.query.delay } set = true - } else if (req.body && typeof req.body.delay !== 'udnefined') { + } + if (req.body && typeof req.body.delay !== 'udnefined') { if (typeof req.body.delay === 'string') { delay = parseInt(req.body.delay) } else { @@ -195,6 +197,20 @@ function rFrame (req, res, next) { exposure = req.body.exposure } } + if (req.query && typeof req.query.delay !== 'undefined') { + if (typeof req.query.delay === 'string') { + delay = parseInt(req.query.delay) + } else { + delay = req.query.delay + } + } + if (req.body && typeof req.body.delay !== 'udnefined') { + if (typeof req.body.delay === 'string') { + delay = parseInt(req.body.delay) + } else { + delay = req.body.delay + } + } log.info('/frame', { method : req.method, dir : dir, exposure : exposure }) intval.frame(dir, exposure, (len) => { res.send({ dir : dir, len : len}) @@ -216,6 +232,51 @@ function rReset (req, res, next) { }, 10) } +function rSequence (req, res, next) { + let dir = true + let exposure = 0 + let delay = 0 + if (req.query && typeof req.query.dir !== 'undefined') { + if (typeof req.query.dir === 'string') { + dir = (req.query.dir === 'true') + } else { + dir = req.query.dir + } + } + if (req.body && typeof req.body.dir !== 'undefined') { + if (typeof req.body.dir === 'string') { + dir = (req.body.dir === 'true') + } else { + dir = req.body.dir + } + } + if (req.query && typeof req.query.exposure !== 'undefined') { + if (typeof req.query.exposure === 'string') { + exposure = parseInt(req.query.exposure) + } else { + exposure = req.query.exposure + } + } + if (req.body && typeof req.body.exposure !== 'undefined') { + if (typeof req.body.exposure === 'string') { + exposure = parseInt(req.body.exposure) + } else { + exposure = req.body.exposure + } + } + if (sequence.active) { + return sequence.stop(() => { + res.send({ stopped : true }) + return next() + }) + } else { + return sequence.start({}, (seq) => { + res.send(seq) + return next() + }) + } +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { From 37326534ed4a0e3bad5cd150b29aeb7118c288f7 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 08:59:49 -0500 Subject: [PATCH 223/414] Accidentally broke web while testing, add back. --- app/www/static/js/intval.web.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 0585796..c1851d4 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -162,10 +162,10 @@ web.useJson = function (res) { return res.json(); } web.init = function () { - //window.frame = web.frame; - //window.getState = web.getState; - //window.setDir = web.setDir; - //window.setExposure = web.setExposure; - //window.setCounter = web.setCounter; + window.frame = web.frame; + window.getState = web.getState; + window.setDir = web.setDir; + window.setExposure = web.setExposure; + window.setCounter = web.setCounter; console.log('started web') }; \ No newline at end of file From 73a65d902169def303b55d9e9f6936df380aaf89 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:19:43 -0500 Subject: [PATCH 224/414] Add sqlite3 and table logging functionality. Track all frames all the time. --- lib/db/index.js | 33 +++++++++++++++++++++++++++ lib/intval/index.js | 46 +++++++++++++++++++++++++++----------- scripts/intval3-install.sh | 2 +- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/lib/db/index.js b/lib/db/index.js index b7ff1bc..882af58 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -1,2 +1,35 @@ 'use strict' +const os = require('os') +const sqlite3 = require('sqlite3').verbose() +const squel = require('squel') +const DB_FILE = os.homedir() + '/.intval3.db' +const db = new sqlite3.Database(DB_FILE) + +class DB { + constructor () { + this._table = 'frames' + this.createTable() + } + createTable () { + const query = `CREATE TABLE + IF NOT EXISTS ${this._table} ( + dir BOOLEAN, + exposure INTEGER, + start INTEGER, + stop INTEGER, + len INTEGER, + counter INTEGER + );` + db.run(query) + } + insert (obj) { + const query = squel.insert() + .into(this._table) + .setFields(obj) //dir, exposure, start, stop, len, counter + .toString() + db.run(query) + } +} + +module.exports = new DB() \ No newline at end of file diff --git a/lib/intval/index.js b/lib/intval/index.js index 23acc5d..9e6f459 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -1,5 +1,6 @@ 'use strict' +const db = require('../db') const log = require('../log')('intval') const storage = require('node-persist') const fs = require('fs') @@ -93,6 +94,7 @@ intval._setState = function (data) { paused : false, exposure : 0, //length of frame exposure, in ms delay : 0, //delay before start of frame, in ms + current : {}, //current settings cb : () => {} }, release : { @@ -178,16 +180,30 @@ intval._pause = function () { * */ intval._stop = function () { - intval._pin.fwd.writeSync(0) - intval._pin.bwd.writeSync(0) + const entry = {} const now = +new Date() const len = now - intval._state.frame.start + intval._pin.fwd.writeSync(0) + intval._pin.bwd.writeSync(0) + log.info(`_stop`, { frame : len }) intval._pin.micro.unwatch() intval._state.frame.active = false + if (intval._state.frame.cb) intval._state.frame.cb(len) + + entry.start = intval._state.frame.start + entry.stop = now + entry.len = len + entry.dir = intval._state.current.dir + entry.exposure = intval._state.frame.current.exposure + entry.counter = intval._state.counter + + db.insert(entry) + + intval._state.frame.current = {} } /** * Callback for watching relese switch state changes. @@ -325,35 +341,35 @@ intval.setCounter = function (val = 0) { /** * Begin a single frame with set variables or defaults * -* @param {?boolean} [dir="null"] (optional) Direction of the frame -* @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum +* @param {?boolean} [dir="null"] (optional) Direction of the frame +* @param {?integer} [exposure="null"] (optional) Exposure time, 0 = minimum * */ -intval.frame = function (dir = null, time = null, cb = () => {}) { +intval.frame = function (dir = null, expsure = null, cb = () => {}) { if (dir === true || (dir === null && intval._state.frame.dir === true) ) { dir = true } else { dir = false } - if (time === null && intval._state.frame.exposure !== 0) { - time = intval._state.frame.exposure - } else if (time === null) { - time = 0 //default speed + if (exposure === null && intval._state.frame.exposure !== 0) { + exposure = intval._state.frame.exposure + } else if (exposure === null) { + exposure = 0 //default speed } intval._state.frame.start = +new Date() intval._state.frame.active = true intval._pin.micro.watch(intval._watchMicro) - log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : time}) + log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : exposure}) if (dir) { intval._startFwd() } else { intval._startBwd() } - if (time !== 0) { + if (exposure !== 0) { intval._state.frame.paused = true if (dir) { setTimeout(intval._pause, intval._frame.open) @@ -361,14 +377,14 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { setTimeout( () => { intval._state.frame.paused = false intval._startFwd() - }, time + intval._frame.closed) + }, exposure + intval._frame.closed) } else { setTimeout(intval._pause, intval._frame.openBwd) setTimeout( () => { //log.info('frame', 'restarting') intval._state.frame.paused = false intval._startBwd() - }, time + intval._frame.closed) + }, exposure + intval._frame.closed) } } if (dir) { @@ -384,6 +400,10 @@ intval.frame = function (dir = null, time = null, cb = () => {}) { cb(len) } } + intval._state.frame.current = { + dir: dir, + exposure: time + } } /** * Start a sequence of frames, using defaults or explicit instructions diff --git a/scripts/intval3-install.sh b/scripts/intval3-install.sh index 1290526..06430fe 100644 --- a/scripts/intval3-install.sh +++ b/scripts/intval3-install.sh @@ -9,7 +9,7 @@ apt-get install nodejs npm -y npm install -g n n 9.1.0 npm install -g npm@latest -npm install -g pm2 +npm install -g pm2 node-gyp echo "Installing bluetooth dependencies..." apt-get install bluetooth bluez libbluetooth-dev libudev-dev -y From bdac1aa50c53849da5ab950798fd50a2a3817882 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:21:43 -0500 Subject: [PATCH 225/414] Need node-gyp to build sqlite3. --- scripts/intval3-deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/intval3-deps.sh b/scripts/intval3-deps.sh index ffe228d..299217b 100644 --- a/scripts/intval3-deps.sh +++ b/scripts/intval3-deps.sh @@ -9,7 +9,7 @@ apt-get install nodejs npm -y npm install -g n n latest npm install -g npm@latest -npm install -g pm2 +npm install -g pm2 node-gyp echo "Installing bluetooth dependencies..." apt-get install bluetooth bluez libbluetooth-dev libudev-dev -y From 320d3fe02fd43108db7dd978d5fa40f14753dedf Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:21:56 -0500 Subject: [PATCH 226/414] Spell exposure right, ya dingus. --- lib/intval/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 9e6f459..0f715c8 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -345,7 +345,7 @@ intval.setCounter = function (val = 0) { * @param {?integer} [exposure="null"] (optional) Exposure time, 0 = minimum * */ -intval.frame = function (dir = null, expsure = null, cb = () => {}) { +intval.frame = function (dir = null, exposure = null, cb = () => {}) { if (dir === true || (dir === null && intval._state.frame.dir === true) ) { dir = true } else { @@ -402,7 +402,7 @@ intval.frame = function (dir = null, expsure = null, cb = () => {}) { } intval._state.frame.current = { dir: dir, - exposure: time + exposure: exposure } } /** From e558b0387dda4058d10e3c4beefc9197226aecb1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:23:05 -0500 Subject: [PATCH 227/414] State.frame.current, not state.current. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 0f715c8..d1d2fed 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -197,7 +197,7 @@ intval._stop = function () { entry.start = intval._state.frame.start entry.stop = now entry.len = len - entry.dir = intval._state.current.dir + entry.dir = intval._state.frame.current.dir entry.exposure = intval._state.frame.current.exposure entry.counter = intval._state.counter From cd3f79a3a00b746dfd1142a0ba76144185b09bce Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:29:44 -0500 Subject: [PATCH 228/414] Use an integer to track direction, boolean is not an sqlite3 datatype. --- lib/db/index.js | 2 +- lib/intval/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/db/index.js b/lib/db/index.js index 882af58..890bc10 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -14,7 +14,7 @@ class DB { createTable () { const query = `CREATE TABLE IF NOT EXISTS ${this._table} ( - dir BOOLEAN, + dir INTEGER, exposure INTEGER, start INTEGER, stop INTEGER, diff --git a/lib/intval/index.js b/lib/intval/index.js index d1d2fed..5cecdff 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -197,7 +197,7 @@ intval._stop = function () { entry.start = intval._state.frame.start entry.stop = now entry.len = len - entry.dir = intval._state.frame.current.dir + entry.dir = intval._state.frame.current.dir ? 1 : 0 entry.exposure = intval._state.frame.current.exposure entry.counter = intval._state.counter From 64389ab954da9a14790b9cb449ad14a026151597 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:34:24 -0500 Subject: [PATCH 229/414] Add feature to list all frames and another to find others with a WHERE statement. --- lib/db/index.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/db/index.js b/lib/db/index.js index 890bc10..2ee0a8c 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -30,6 +30,19 @@ class DB { .toString() db.run(query) } + find (where, cb) { + const query.select() + .from(this._table) + .where(where) + .toString() + db.all(query, cb) + } + list (cb) { + const query.select() + .from(this._table) + .toString() + db.all(query, cb) + } } module.exports = new DB() \ No newline at end of file From 6ee022a2a49a9bd5477d7c1b21f8c093ffc05b6f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:35:47 -0500 Subject: [PATCH 230/414] Had not declared query const properly. Fixed. --- lib/db/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/db/index.js b/lib/db/index.js index 2ee0a8c..f8c9c18 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -31,14 +31,14 @@ class DB { db.run(query) } find (where, cb) { - const query.select() + const query = squel.select() .from(this._table) .where(where) .toString() db.all(query, cb) } list (cb) { - const query.select() + const query = squel.select() .from(this._table) .toString() db.all(query, cb) From 6ce6af3abe13cd48c4986f61dc2eda0e59503a09 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:46:18 -0500 Subject: [PATCH 231/414] Log whether or not frame is part of a sequence. --- lib/db/index.js | 3 ++- lib/intval/index.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/db/index.js b/lib/db/index.js index f8c9c18..b391538 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -19,7 +19,8 @@ class DB { start INTEGER, stop INTEGER, len INTEGER, - counter INTEGER + counter INTEGER, + sequence INTEGER );` db.run(query) } diff --git a/lib/intval/index.js b/lib/intval/index.js index 5cecdff..5a0e05e 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -105,7 +105,8 @@ intval._setState = function (data) { time : 0, primed : false //is ready to stop frame }, - counter : 0 + counter : 0, + sequence : false } intval._storeState() } @@ -200,6 +201,7 @@ intval._stop = function () { entry.dir = intval._state.frame.current.dir ? 1 : 0 entry.exposure = intval._state.frame.current.exposure entry.counter = intval._state.counter + entry.sequence = intbal._state.sequence ? 1 : 0 db.insert(entry) From a71deca9e3309dce080a1b8d66328983eb7c3dbe Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:52:49 -0500 Subject: [PATCH 232/414] INTBAL?????? --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 5a0e05e..04b3831 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -201,7 +201,7 @@ intval._stop = function () { entry.dir = intval._state.frame.current.dir ? 1 : 0 entry.exposure = intval._state.frame.current.exposure entry.counter = intval._state.counter - entry.sequence = intbal._state.sequence ? 1 : 0 + entry.sequence = intval._state.sequence ? 1 : 0 db.insert(entry) From 292dc2913c50a500853f9aea9bb4867255cdab0b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 23 Nov 2017 09:53:04 -0500 Subject: [PATCH 233/414] Do not watch locally --- process.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process.json b/process.json index d074b23..f62bc31 100644 --- a/process.json +++ b/process.json @@ -3,7 +3,7 @@ { "name" : "intval3", "script" : "./index.js", - "watch" : true, + "watch" : false, "env" : { "BLENO_DEVICE_NAME" : "intval3", "DEVICE_ID" : "intval3", From fb1273614cbbbc092f66c6cdbf661e2f3be6fa8d Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Tue, 28 Nov 2017 15:20:52 -0500 Subject: [PATCH 234/414] Example sequencer control with a bash script. --- scripts/sequence.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 scripts/sequence.sh diff --git a/scripts/sequence.sh b/scripts/sequence.sh new file mode 100644 index 0000000..c2abdb9 --- /dev/null +++ b/scripts/sequence.sh @@ -0,0 +1,10 @@ +#!/bin/bash +URL=$1 +COUNTER=0 +FRAMES=25 +while [ $COUNTER -lt $FRAMES ]; do + echo The counter is $COUNTER + curl "$URL/frame" + sleep 60 + ((COUNTER++)) +done From 58f2ec885a299e69da78a450d1321440c1218b8c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Tue, 28 Nov 2017 15:21:52 -0500 Subject: [PATCH 235/414] Enlarge the panel mount column --- hardware/intval3.scad | 6 +++--- hardware/mount.scad | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hardware/intval3.scad b/hardware/intval3.scad index 190ed72..117c2bf 100644 --- a/hardware/intval3.scad +++ b/hardware/intval3.scad @@ -39,9 +39,9 @@ module exploded_view () { //bolex_pin_laser(0, 0); //intval_laser_standoffs_plate(); //intval_electronics_mount("METRO"); -//motor_mount_bottom(); +motor_mount_bottom(); //projection () intval_panel_laser(); -intval_laser_panel_cover(true, ALL_RED=true); +//intval_laser_panel_cover(true, ALL_RED=true); //rotate([0, 0, 13]) intval_panel_laser(); //rotate([0, 0, 13]) intval_laser_panel_cover(); //key_cap(); @@ -50,7 +50,7 @@ intval_laser_panel_cover(true, ALL_RED=true); //motor_key_120(); //plunger_plate(); //motor_cap(false); -motor_cap_120(false); +//motor_cap_120(false); //translate([0, 0, 39 / 2 + 5.75]) bolt_guide(); //exploded_view(); diff --git a/hardware/mount.scad b/hardware/mount.scad index 69edc6e..d9b4283 100644 --- a/hardware/mount.scad +++ b/hardware/mount.scad @@ -192,7 +192,7 @@ module motor_mount_bottom () { module panel_attachment () { difference () { union() { - translate([0, 0, 7.75 + 3]) cylinder(r = 7/2, h = 44 - shelf_h, center = true); + translate([0, 0, 7.75 + 3]) cylinder(r = 10/2, h = 44 - shelf_h, center = true); translate([3.5, 0, 0]) cube([7, 7, height - shelf_h - 4], center = true); } translate([0, 0, 25]) cylinder(r = 3.2/2, h = 50, center = true); From 395c60b8a28b92662d11c061faffe3cabfb5f511 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 6 Dec 2017 23:13:49 -0500 Subject: [PATCH 236/414] Key_cap sizing improved --- hardware/case.scad | 14 +++++++++----- hardware/intval3.scad | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hardware/case.scad b/hardware/case.scad index cd25cda..01edace 100644 --- a/hardware/case.scad +++ b/hardware/case.scad @@ -406,12 +406,16 @@ module bearing (x, y, z, width= 8, hole = true, calval = 0) { } module key_cap () { - $fn = 40; - outerD = 22.1; - fuzz = 0.1; + $fn = 60; + thickness = .75; + innerD = 22.1; + outerD = innerD + (thickness * 2); + h = 18 - 2.5; + difference () { - cylinder(r = outerD / 2 + fuzz + 1, h = 18, center = true); - translate([0, 0, -1]) cylinder(r = outerD / 2, h = 16, center = true); + cylinder(r = outerD / 2, h = h, center = true); + translate([0, 0, -1.01]) cylinder(r = innerD / 2, h = h - thickness, center = true); + //translate([100, 0, 0]) cube([200, 200, 200], center = true); } //decoys(23, 7); } diff --git a/hardware/intval3.scad b/hardware/intval3.scad index 117c2bf..01051c2 100644 --- a/hardware/intval3.scad +++ b/hardware/intval3.scad @@ -39,12 +39,12 @@ module exploded_view () { //bolex_pin_laser(0, 0); //intval_laser_standoffs_plate(); //intval_electronics_mount("METRO"); -motor_mount_bottom(); +//motor_mount_bottom(); //projection () intval_panel_laser(); //intval_laser_panel_cover(true, ALL_RED=true); //rotate([0, 0, 13]) intval_panel_laser(); //rotate([0, 0, 13]) intval_laser_panel_cover(); -//key_cap(); +key_cap(); //geared_motor_mount_120(); //motor_key(); //motor_key_120(); From f1a5ae5affc92a1b564d32733746948af8621c2a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 7 Dec 2017 12:05:54 -0500 Subject: [PATCH 237/414] bluetooth on data event --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 06395b8..ee66fd8 100644 --- a/index.js +++ b/index.js @@ -290,9 +290,9 @@ function index (req, res, next) { function init () { createServer() - /*ble.on('data', (str) => { + ble.on('data', (str) => { console.log(str) - })*/ + }) intval.init() } From 1fef6d69251f93b953ea6df4123cfab6a551388a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 11 Dec 2017 14:38:46 -0500 Subject: [PATCH 238/414] Pass an intval.state object into ble lib --- index.js | 9 ++++++++- lib/ble/index.js | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ee66fd8..70c95d8 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const restify = require('restify') const log = require('./lib/log')('main') const fs = require('fs') -const ble = require('./lib/ble') +const BLE = require('./lib/ble') const intval = require('./lib/intval') const sequence = require('./lib/sequence') @@ -18,6 +18,8 @@ let app = restify.createServer({ version: '0.0.1' }) +let ble + function createServer () { app.use(restify.plugins.queryParser()) app.use(restify.plugins.bodyParser({ mapParams: false })) @@ -287,6 +289,10 @@ function index (req, res, next) { }) } +function bleGetState () { + return intval.state +} + function init () { createServer() @@ -294,6 +300,7 @@ function init () { console.log(str) }) intval.init() + ble = new BLE(bleGetState) } init() diff --git a/lib/ble/index.js b/lib/ble/index.js index 2349bbd..3312aed 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -19,6 +19,7 @@ const NETWORK = os.networkInterfaces() const MAC = getMac() || spoofMac() let currentWifi = 'disconnected' +let getState const chars = [] @@ -123,9 +124,11 @@ class BLE { * * @constructor */ - constructor () { + constructor (bleGetState) { log.info('Starting bluetooth service') + getState = bleGetState + bleno.on('stateChange', state => { const BLE_ID = `${DEVICE_ID}_${MAC}` log.info('stateChange', { state : state }) @@ -159,7 +162,7 @@ class BLE { }) } _onWrite (data, offset, withoutResponse, callback) { - let result + let result = {} let utf8 let obj if (offset) { @@ -168,6 +171,7 @@ class BLE { return callback(result) } utf8 = data.toString('utf8') + obj = JSON.parse(utf8) console.dir(obj) result = bleno.Characteristic.RESULT_SUCCESS @@ -175,6 +179,7 @@ class BLE { } _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS + const state = getState() const data = new Buffer(JSON.stringify( { success : true } )) callback(result, data.slice(offset, data.length)) } @@ -187,6 +192,7 @@ class BLE { on (eventName, callback) { this[`_on${capitalize(eventName)}`] = callback } + } -module.exports = new BLE() \ No newline at end of file +module.exports = BLE \ No newline at end of file From 139c72caee243a8985dbdfe3f03c1be246c0353f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 11 Dec 2017 14:41:44 -0500 Subject: [PATCH 239/414] ble needs to be constructed --- index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 70c95d8..2099f11 100644 --- a/index.js +++ b/index.js @@ -295,12 +295,11 @@ function bleGetState () { function init () { createServer() - + intval.init() + ble = new BLE(bleGetState) ble.on('data', (str) => { console.log(str) }) - intval.init() - ble = new BLE(bleGetState) } init() From 5f97c7a4deface1369ef282da693bcb0ecc3bc03 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 11 Dec 2017 14:44:27 -0500 Subject: [PATCH 240/414] Pass back actual state object. --- lib/ble/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 3312aed..b644608 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -180,7 +180,7 @@ class BLE { _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS const state = getState() - const data = new Buffer(JSON.stringify( { success : true } )) + const data = new Buffer(JSON.stringify( state )) callback(result, data.slice(offset, data.length)) } /** From 76fcc953272c270b490750d998146776f95b07bd Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 11 Dec 2017 14:47:05 -0500 Subject: [PATCH 241/414] See what state is before sending --- lib/ble/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ble/index.js b/lib/ble/index.js index b644608..bbe4e52 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -180,6 +180,8 @@ class BLE { _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS const state = getState() + console.log('getState') + console.dir(state) const data = new Buffer(JSON.stringify( state )) callback(result, data.slice(offset, data.length)) } From 3e9f560db03fc804a4d5fb602fa53458e4473108 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 11 Dec 2017 14:57:01 -0500 Subject: [PATCH 242/414] intval.status() not intval.state. --- index.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 2099f11..338c1b0 100644 --- a/index.js +++ b/index.js @@ -289,14 +289,12 @@ function index (req, res, next) { }) } -function bleGetState () { - return intval.state -} - function init () { createServer() intval.init() - ble = new BLE(bleGetState) + ble = new BLE(() => { + return intval.status() + }) ble.on('data', (str) => { console.log(str) }) From 5f61865366ff9e4a95507e0fa82316498d791b2b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 11:38:48 -0500 Subject: [PATCH 243/414] No need to log state --- lib/ble/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index bbe4e52..b644608 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -180,8 +180,6 @@ class BLE { _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS const state = getState() - console.log('getState') - console.dir(state) const data = new Buffer(JSON.stringify( state )) callback(result, data.slice(offset, data.length)) } From 05a69e55126861f2bbefbcfdcbc3d6e603f22b2a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 11:54:43 -0500 Subject: [PATCH 244/414] Do not log getState data, but do log utf8 string and parsed object onWrite --- lib/ble/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index b644608..864c7bb 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -171,7 +171,7 @@ class BLE { return callback(result) } utf8 = data.toString('utf8') - + console.log(uft8) obj = JSON.parse(utf8) console.dir(obj) result = bleno.Characteristic.RESULT_SUCCESS From f74465cbbc7de06a0a41a354effb7b406325effd Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 11:57:04 -0500 Subject: [PATCH 245/414] utf8 not uft8. I am a freaking dumbass. --- lib/ble/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 864c7bb..4878379 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -171,7 +171,7 @@ class BLE { return callback(result) } utf8 = data.toString('utf8') - console.log(uft8) + console.log(utf8) obj = JSON.parse(utf8) console.dir(obj) result = bleno.Characteristic.RESULT_SUCCESS From 4aadc5a9df55ea3a0fd5873c2c4aac70b993b25f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 13:09:13 -0500 Subject: [PATCH 246/414] Use callback model for bluetooth write events. --- index.js | 24 +++++++++++++++--------- lib/ble/index.js | 10 +++++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 338c1b0..0221199 100644 --- a/index.js +++ b/index.js @@ -41,10 +41,14 @@ function createServer () { }) } -function bleBindings () { - +function createBLE () { + ble = new BLE(() => { + return intval.status() + }) + ble.on('frame', bFrame) } +//Restify functions function rDir (req, res, next) { let dir = true let set = false @@ -279,6 +283,13 @@ function rSequence (req, res, next) { } } +//Ble functions + +function bFrame (cb) { + console.log(str) + cb({ dir: true, len : 630 }) +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { @@ -290,14 +301,9 @@ function index (req, res, next) { } function init () { - createServer() intval.init() - ble = new BLE(() => { - return intval.status() - }) - ble.on('data', (str) => { - console.log(str) - }) + createServer() + createBLE() } init() diff --git a/lib/ble/index.js b/lib/ble/index.js index 4878379..cf52e67 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -171,11 +171,15 @@ class BLE { return callback(result) } utf8 = data.toString('utf8') - console.log(utf8) obj = JSON.parse(utf8) - console.dir(obj) result = bleno.Characteristic.RESULT_SUCCESS - return callback(result) + if (obj.type && this[`_on${capitalize(obj.type)}`]) { + this[`_on${capitalize(obj.type)}`]( data => { + return callback(result, data.slice(offset, data.length)) + }) + } else { + return callback(result) + } } _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS From 9d95ffa483b48a48072f7b60fdcfc54496e7da24 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 13:14:20 -0500 Subject: [PATCH 247/414] Was trying to console.log an undefined string. --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 0221199..e6c6f50 100644 --- a/index.js +++ b/index.js @@ -286,8 +286,9 @@ function rSequence (req, res, next) { //Ble functions function bFrame (cb) { - console.log(str) - cb({ dir: true, len : 630 }) + let dir = intval._state.frame.dir + let len = 630 + cb({ dir: dir, len : len }) } function index (req, res, next) { From 601d0cf581d3ad0f354734bc73b21da06a8b95f1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 13:24:26 -0500 Subject: [PATCH 248/414] Send a buffer of a string, not the entire string. --- lib/ble/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index cf52e67..ee7e4d9 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -174,7 +174,9 @@ class BLE { obj = JSON.parse(utf8) result = bleno.Characteristic.RESULT_SUCCESS if (obj.type && this[`_on${capitalize(obj.type)}`]) { - this[`_on${capitalize(obj.type)}`]( data => { + this[`_on${capitalize(obj.type)}`]( obj => { + let str = JSON.stringify(obj) + let data = new Buffer(str) return callback(result, data.slice(offset, data.length)) }) } else { From d8e6a2206e11bacaf2a4d8760d657bab2cce640d Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 13:37:36 -0500 Subject: [PATCH 249/414] OK! Cannot pass data back during a write event, BUUUT can respond asynchronously, and state can be read immediately after? Or should I cache a response? More on this later. --- index.js | 5 +++-- lib/ble/index.js | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index e6c6f50..a4bb81f 100644 --- a/index.js +++ b/index.js @@ -285,10 +285,11 @@ function rSequence (req, res, next) { //Ble functions -function bFrame (cb) { +function bFrame (obj, cb) { let dir = intval._state.frame.dir let len = 630 - cb({ dir: dir, len : len }) + console.dir(obj) + setTimeout(cb, 630) } function index (req, res, next) { diff --git a/lib/ble/index.js b/lib/ble/index.js index ee7e4d9..e5406a0 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -165,6 +165,7 @@ class BLE { let result = {} let utf8 let obj + let fn if (offset) { log.warn(`Offset scenario`) result = bleno.Characteristic.RESULT_ATTR_NOT_LONG @@ -173,15 +174,15 @@ class BLE { utf8 = data.toString('utf8') obj = JSON.parse(utf8) result = bleno.Characteristic.RESULT_SUCCESS - if (obj.type && this[`_on${capitalize(obj.type)}`]) { - this[`_on${capitalize(obj.type)}`]( obj => { - let str = JSON.stringify(obj) - let data = new Buffer(str) - return callback(result, data.slice(offset, data.length)) + fn = `_on${capitalize(obj.type)}` + if (obj.type && this[fn]) { + return this[fn](obj, () => { + callback(result) }) } else { return callback(result) } + } _onRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS From ae03b97f9619b34c302e8b242e99f9ab100f42f8 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 12 Dec 2017 14:23:14 -0500 Subject: [PATCH 250/414] Set direction with bluetooth, also resolve an unfortunate bug where I repeatedly spelled "undefined" as "udnefined" --- index.js | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index a4bb81f..51443a0 100644 --- a/index.js +++ b/index.js @@ -46,6 +46,7 @@ function createBLE () { return intval.status() }) ble.on('frame', bFrame) + ble.on('dir', bDir) } //Restify functions @@ -59,7 +60,7 @@ function rDir (req, res, next) { dir = req.query.dir } set = true - } else if (req.body && typeof req.body.dir !== 'udnefined') { + } else if (req.body && typeof req.body.dir !== 'undefined') { if (typeof req.body.dir === 'string') { dir = (req.body.dir === 'true') } else { @@ -87,7 +88,7 @@ function rExposure (req, res, next) { exposure = req.query.exposure } set = true - } else if (req.body && typeof req.body.exposure !== 'udnefined') { + } else if (req.body && typeof req.body.exposure !== 'undefined') { if (typeof req.body.exposure === 'string') { exposure = parseInt(req.body.exposure) } else { @@ -119,7 +120,7 @@ function rDelay (req, res, next) { } set = true } - if (req.body && typeof req.body.delay !== 'udnefined') { + if (req.body && typeof req.body.delay !== 'undefined') { if (typeof req.body.delay === 'string') { delay = parseInt(req.body.delay) } else { @@ -210,7 +211,7 @@ function rFrame (req, res, next) { delay = req.query.delay } } - if (req.body && typeof req.body.delay !== 'udnefined') { + if (req.body && typeof req.body.delay !== 'undefined') { if (typeof req.body.delay === 'string') { delay = parseInt(req.body.delay) } else { @@ -286,12 +287,44 @@ function rSequence (req, res, next) { //Ble functions function bFrame (obj, cb) { - let dir = intval._state.frame.dir - let len = 630 - console.dir(obj) + let dir = true + let exposure = 0 + + if (intval._state.frame.dir !== true) { + dir = false + } + if (typeof obj.dir !== 'undefined') { + if (typeof obj.dir === 'string') { + dir = (obj.dir === 'true') + } else { + dir = obj.dir + } + } + if (typeof obj.dir !== 'undefined') { + exposure + } + log.info('frame', { method : 'ble', dir : dir, exposure : exposure }) + /*intval.frame(dir, exposure, (len) => { + return cb() + })*/ setTimeout(cb, 630) } +function bDir (obj, cb) { + let dir = true + let set = false + if (obj.dir !== 'undefined') { + if (typeof obj.dir === 'string') { + dir = (obj.dir === 'true') + } else { + dir = obj.dir + } + } + intval.setDir(dir) + log.info('dir', { method: 'ble', dir : dir }) + cb() +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { From 7fc84a3f14a3b3a1758b3212644a29086e378c5f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 16:04:24 -0500 Subject: [PATCH 251/414] Set exposure with bluetooth app --- index.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 51443a0..7a4f8f1 100644 --- a/index.js +++ b/index.js @@ -47,6 +47,7 @@ function createBLE () { }) ble.on('frame', bFrame) ble.on('dir', bDir) + ble.on('exposure', bExposure) } //Restify functions @@ -300,8 +301,12 @@ function bFrame (obj, cb) { dir = obj.dir } } - if (typeof obj.dir !== 'undefined') { - exposure + if (typeof obj.exposure !== 'undefined') { + if (typeof obj.exposure === 'string') { + exposure = parseInt(obj.exposure) + } else { + exposure = obj.exposure + } } log.info('frame', { method : 'ble', dir : dir, exposure : exposure }) /*intval.frame(dir, exposure, (len) => { @@ -325,6 +330,20 @@ function bDir (obj, cb) { cb() } +function bExposure (obj, cb) { + let exposure = 0 + if (typeof obj.exposure !== 'undefined') { + if (typeof obj.exposure === 'string') { + exposure = parseInt(obj.exposure) + } else { + exposure = req.body.exposure + } + } + intval.setExposure(exposure) + log.info('exposure', { method: 'ble', exposure : exposure }) + return cb() +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { From 2a28b622f0d4baf4b08949b107523c65a0d1e634 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 16:22:22 -0500 Subject: [PATCH 252/414] Set delay from ble --- index.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 7a4f8f1..b85c468 100644 --- a/index.js +++ b/index.js @@ -48,6 +48,7 @@ function createBLE () { ble.on('frame', bFrame) ble.on('dir', bDir) ble.on('exposure', bExposure) + ble.on('delay', bDelay) } //Restify functions @@ -312,7 +313,7 @@ function bFrame (obj, cb) { /*intval.frame(dir, exposure, (len) => { return cb() })*/ - setTimeout(cb, 630) + setTimeout(cb, exposure === 0 ? 630 : exposure) } function bDir (obj, cb) { @@ -344,6 +345,22 @@ function bExposure (obj, cb) { return cb() } +function bDelay (obj, cb) { + let delay = 0 + let set = false + if (typeof obj.delay !== 'undefined') { + if (typeof obj.delay === 'string') { + delay = parseInt(obj.delay) + } else { + delay = obj.delay + } + set = true + } + intval.setDelay(delay) + log.info('delay', { method: 'ble', delay : delay }) + return cb() +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { From 6b65168f031a658a9d18673d32a1d62d9ce9aa53 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 16:25:23 -0500 Subject: [PATCH 253/414] Obj not req --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index b85c468..e9df626 100644 --- a/index.js +++ b/index.js @@ -337,7 +337,7 @@ function bExposure (obj, cb) { if (typeof obj.exposure === 'string') { exposure = parseInt(obj.exposure) } else { - exposure = req.body.exposure + exposure = obj.exposure } } intval.setExposure(exposure) From 2818bd1180012d0ae85aa2c8ae340ab81fa09d35 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 17:01:22 -0500 Subject: [PATCH 254/414] Respect exposure settings. --- index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.js b/index.js index e9df626..07911d7 100644 --- a/index.js +++ b/index.js @@ -295,6 +295,9 @@ function bFrame (obj, cb) { if (intval._state.frame.dir !== true) { dir = false } + if (intval._state.frame.exposure !== 0) { + exposure = intval._state.frame.exposure + } if (typeof obj.dir !== 'undefined') { if (typeof obj.dir === 'string') { dir = (obj.dir === 'true') From 52194e0b4146371f81de733a0757014aebf073bb Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 17:27:42 -0500 Subject: [PATCH 255/414] Set counter via ble --- index.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/index.js b/index.js index 07911d7..c2159cb 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,7 @@ function createBLE () { ble.on('dir', bDir) ble.on('exposure', bExposure) ble.on('delay', bDelay) + ble.on('counter', bCounter) } //Restify functions @@ -364,6 +365,20 @@ function bDelay (obj, cb) { return cb() } +function bCounter (obj, cb) { + let counter = 0 + if (typeof obj.counter !== 'undefined') { + if (typeof obj.counter !== 'string') { + counter = parseInt(obj.counter) + } else { + counter = obj.counter + } + } + intval.setCounter(counter) + log.info('counter', { method : 'ble', counter : counter }) + return cb() +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { From e5207a8474fb0503582f19ee7a6cb8691a272fe2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 17:52:25 -0500 Subject: [PATCH 256/414] App supports controlling intval with bluetooth --- app/config.xml | 4 +- app/package-lock.json | 15 +- app/package.json | 6 +- app/www/index.html | 34 +++-- app/www/static/css/index.css | 34 ++++- app/www/static/js/index.js | 1 - app/www/static/js/intval.core.js | 71 ++++++++- app/www/static/js/intval.mobile.js | 222 ++++++++++++++++++++++++++--- app/www/static/js/intval.web.js | 27 +--- app/www/static/js/spin.min.js | 2 + package-lock.json | 12 +- 11 files changed, 354 insertions(+), 74 deletions(-) create mode 100644 app/www/static/js/spin.min.js diff --git a/app/config.xml b/app/config.xml index cb85e75..18ccb9e 100644 --- a/app/config.xml +++ b/app/config.xml @@ -22,8 +22,10 @@ + + + - diff --git a/app/package-lock.json b/app/package-lock.json index fb5edb4..59fe73e 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4,15 +4,10 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "android-versions": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz", - "integrity": "sha512-k6zlrtWbJ3tx1ZsyyJ0Bo3r6cqPA3JUnFGv7pnIaLr1XVxSi2Tcem2lg3kBebFp27v/A40tZqdlouPyakpyKrw==" - }, "cordova-android": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-6.3.0.tgz", - "integrity": "sha1-2lQYQz0lx1pZd7QoJEu+Q30BKNI=", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-6.4.0.tgz", + "integrity": "sha1-VK6NpXKKjX5e/MYXLT3MoXvH/n0=", "requires": { "android-versions": "1.2.1", "cordova-common": "2.1.0", @@ -27,6 +22,10 @@ "version": "1.1.0", "bundled": true }, + "android-versions": { + "version": "1.2.1", + "bundled": true + }, "ansi": { "version": "0.3.1", "bundled": true diff --git a/app/package.json b/app/package.json index b98c52b..b896cc6 100644 --- a/app/package.json +++ b/app/package.json @@ -10,7 +10,7 @@ "author": "M McWilliams", "license": "MIT", "dependencies": { - "cordova-android": "^6.3.0", + "cordova-android": "^6.4.0", "cordova-ios": "^4.5.4", "cordova-plugin-ble-central": "^1.1.4", "cordova-plugin-compat": "^1.2.0", @@ -30,8 +30,8 @@ } }, "platforms": [ - "ios", - "android" + "android", + "ios" ] } } \ No newline at end of file diff --git a/app/www/index.html b/app/www/index.html index 7a63684..7298e13 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -4,12 +4,15 @@ INTVAL - + +
    +
    +

    INTVAL

    @@ -27,7 +30,7 @@
    Exposure 1/5
    - + +
    -

    BLUETOOTH

    - -

    WIFI

    -
    - +
    +

    BLUETOOTH

    +
    -
    - +
    +

    WIFI

    +
    + +
    +
    + +
    @@ -92,6 +99,7 @@
    + diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 730c2c5..d64fc43 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -46,6 +46,12 @@ body{ .page.selected{ display: block; } +.ble{ + display: none; +} +.ble.active{ + display: block; +} h2{ font-size: 18px; text-align: center; @@ -144,7 +150,8 @@ button{ padding: 5px 0; text-align: center; } -button:focus{ +button:focus, +button.focus{ background-color: #20ce45; border-color: #20ce45; color: #212121; @@ -162,7 +169,7 @@ button:focus{ right: 10%; } .label{ - text-align: center; + /*text-align: center;*/ color: #666; margin-top: 6px; margin-bottom: 9px; @@ -244,3 +251,26 @@ footer > div.selected{ #compile{ margin-top: 20px; } + +#seq{ + margin-top: 40px; +} + +#overlay{ + position: fixed; + z-index: 2001; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: none; +} +#overlay.active{ + display: block; +} +#spinner{ + margin-top: 200px; +} diff --git a/app/www/static/js/index.js b/app/www/static/js/index.js index d7c73a0..1c8de02 100644 --- a/app/www/static/js/index.js +++ b/app/www/static/js/index.js @@ -35,7 +35,6 @@ var app = { // 'pause', 'resume', etc. onDeviceReady: function() { mobile.init(); - getState(); }, onDeviceResume : function () { getState(); diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index c6335bc..81dee9c 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -12,6 +12,7 @@ const STATE = { delayScale : 'ms', counter : 0 }; + //functions window.frame = null; window.getState = null; @@ -93,7 +94,7 @@ var setExposureScale = function () { }; var setDelayScale = function () { - const scale = document.getElementById('scale').value; + const scale = document.getElementById('delayScale').value; const elem = document.getElementById('delay'); if (scale === 'ms') { elem.value = STATE.delay; @@ -138,6 +139,36 @@ var unsetPages = function () { } }; + +var setState = function (res) { + let exposure; + let exposureScale; + let delayScale; + + if (res.frame.dir !== true) { + document.getElementById('dir').checked = true; + STATE.dir = res.frame.dir; + setDirLabel(false); + } + document.getElementById('counter').value = res.counter; + STATE.counter = res.counter; + //Exposure + if (res.frame.exposure === 0) { + res.frame.exposure = BOLEX.expected; + } + STATE.exposure = res.frame.exposure; + exposure = shutter(STATE.exposure); + exposureScale = scaleAuto(STATE.exposure); + + document.getElementById('str').value = exposure.str; + document.getElementById('scale').value = exposureScale; + setExposureScale(); + + STATE.delay = res.frame.delay; + delayScale = scaleAuto(STATE.delay); + document.getElementById('delayScale').value = delayScale; + setDelayScale(); +}; var appPage = function () { unsetPages(); document.getElementById('app').classList.add('selected'); @@ -154,6 +185,44 @@ var mscriptPage = function () { document.getElementById('mscriptIcon').classList.add('selected'); editor.cm.refresh(); }; +var spinnerInit = function () { + const spinnerOpts = { + lines: 13 // The number of lines to draw + , length: 33 // The length of each line + , width: 11 // The line thickness + , radius: 30 // The radius of the inner circle + , scale: 0.5 // Scales overall size of the spinner + , corners: 1 // Corner roundness (0..1) + , color: '#fff' // #rgb or #rrggbb or array of colors + , opacity: 0.25 // Opacity of the lines + , rotate: 0 // The rotation offset + , direction: 1 // 1: clockwise, -1: counterclockwise + , speed: 1 // Rounds per second + , trail: 60 // Afterglow percentage + , fps: 20 // Frames per second when using setTimeout() as a fallback for CSS + , zIndex: 2e9 // The z-index (defaults to 2000000000) + , className: 'spinner' // The CSS class to assign to the spinner + , top: '50%' // Top position relative to parent + , left: '50%' // Left position relative to parent + , shadow: true // Whether to render a shadow + , hwaccel: true // Whether to use hardware acceleration + , position: 'relative' // Element positioning + }; + const target = document.getElementById('spinner'); + const spinner = new Spinner(spinnerOpts).spin(target); +}; +var spinnerShow = function () { + const elem = document.getElementById('overlay'); + if (!elem.classList.contains('active')) { + elem.classList.add('active'); + } +}; +var spinnerHide = function () { + const elem = document.getElementById('overlay'); + if (elem.classList.contains('active')) { + elem.classList.remove('active'); + } +} var isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); }; \ No newline at end of file diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index a067145..46669ce 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -7,27 +7,72 @@ mobile.ble = { SERVICE_ID : '149582bd-d49d-4b5c-acd1-1ae503d09e7a', CHAR_ID : '47bf69fb-f62f-4ef8-9be8-eb727a54fae4', //general data WIFI_ID : '3fe7d9cf-7bd2-4ff0-97c5-ebe87288c2cc', //wifi only - DEVICES : [] + devices : [], + device : {}, + connected : false, + active : false }; mobile.ble.scan = function () { - ble.scan([], 5, mobile.ble.onDiscover, BLE.onError); + spinnerShow(); + ble.scan([], 5, mobile.ble.onDiscover, mobile.ble.onError); + mobile.ble.devices = []; + setTimeout(() => { + if (!mobile.ble.connected) { + ble.stopScan(() => {}, mobile.ble.onError); + spinnerHide(); + alert('No INTVAL devices found.'); + } + }, 5000) }; mobile.ble.onDiscover = function (device) { - console.dir(device); - mobile.ble.connect(device.id); + if (device && device.name && device.name === 'intval3') { + console.log('BLE - Discovered INTVAL'); + console.dir(device); + mobile.ble.devices.push(device); + if (!mobile.ble.connected) { + mobile.ble.connect(device); + } + } else { + //console.log(`BLE - Discovered Other ${device.id}`); + } } -mobile.ble.connect = function (deviceId) { - ble.connect(deviceId, function (peripheral) { - mobile.ble.onConnect(peripheral, deviceId); +mobile.ble.connect = function (device) { + console.log(`BLE - Connecting to ${device.id}`) + ble.connect(device.id, (peripheral) => { + mobile.ble.onConnect(peripheral, device); }, mobile.ble.onError); }; -mobile.ble.onConnect = function (peripheral, deviceId) { +mobile.ble.onConnect = function (peripheral, device) { + spinnerHide(); + ble.stopScan(() => {}, moble.ble.onError); + console.log(`BLE - Connected to ${device.id}`); console.log(peripheral); - console.log(deviceId); + console.dir(device); + mobile.ble.device = device; + mobile.ble.connected = true; + + getState(); +}; + +mobile.ble.disconnect = function () { + let device + if (!mobile.ble.connected) { + console.warn('Not connected to any device') + return false + } + device = mobile.ble.device + console.log(`BLE - Disconnecting from ${device.id}`) + ble.disconnect(device.id, mobile.ble.onDisconnect, mobile.ble.onDisconnect); +}; + +mobile.ble.onDisconnect = function (res) { + console.log(`BLE - Disconnected from ${res}`); + mobile.ble.connected = false; + mobile.ble.device = {}; }; mobile.ble.onError = function (err) { @@ -35,15 +80,154 @@ mobile.ble.onError = function (err) { }; mobile.init = function () { - frame = mobile.frame; - getState = mobile.getState; - setDir = mobile.setDir; - setExposure = mobile.setExposure; - setCounter = mobile.setCounter; + const bleInputs = document.querySelectorAll('.ble') + + window.frame = mobile.frame; + window.getState = mobile.getState; + window.setDir = mobile.setDir; + window.setExposure = mobile.setExposure; + window.setDelay = mobile.setDelay; + window.setCounter = mobile.setCounter; + + //show ble-specific fields in settings + for (let i of bleInputs) { + i.classList.add('active'); + } + spinnerInit(); + mobile.ble.scan(); }; -mobile.frame = function () {}; -mobile.getState = function () {}; -mobile.setDir = function () {}; -mobile.setExposure = function () {}; -mobile.setCounter = function () {}; \ No newline at end of file +mobile.getState = function () { + if (!mobile.ble.connected) { + // + } + ble.read(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + mobile.stateSuccess, + mobile.ble.onError); +}; +mobile.stateSuccess = function (data) { + let str = bytesToString(data); + let res = JSON.parse(str); + setState(res) +}; + +mobile.frame = function () { + const opts = { + type : 'frame' + }; + if (!mobile.ble.connected) { + return alert('Not connected to an INTVAL device.'); + } + if (mobile.ble.active) { + return false; + } + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.frameSuccess, + mobile.ble.onError); + document.getElementById('frame').classList.add('focus'); + mobile.ble.active = true; +}; + + +mobile.frameSuccess = function () { + console.log('Frame finished, getting state.'); + mobile.ble.active = false; + document.getElementById('frame').classList.remove('focus'); + mobile.getState(); +} +mobile.setDir = function () { + const opts = { + type : 'dir', + dir : !document.getElementById('dir').checked + }; + + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.dirSuccess, + mobile.ble.onError); +}; +mobile.dirSuccess = function () { + console.log('Set direction'); + mobile.getState(); +}; +mobile.setExposure = function () { + let exposure = document.getElementById('exposure').value; + let scaledExposure; + let opts = { + type : 'exposure' + }; + if (exposure === '' || exposure === null) { + exposure = 0; + } + scaledExposure = scaleTime(exposure, STATE.scale); + opts.exposure = scaledExposure; + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.exposureSuccess, + mobile.ble.onError); +}; +mobile.exposureSuccess = function () { + console.log('Set exposure'); + mobile.getState(); +}; + +mobile.setDelay = function () { + const delay = document.getElementById('delay').value; + const scaledDelay = scaleTime(delay, STATE.delayScale) + let opts = { + type : 'delay', + delay : scaledDelay + }; + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.delaySuccess, + mobile.ble.onError); +} + +mobile.delaySuccess = function () { + console.log('Set delay') + mobile.getState(); +}; + +mobile.setCounter = function () { + const counter = document.getElementById('counter').value; + const change = prompt(`Change counter value?`, counter); + if (change === null || !isNumeric(change)) return false; + let opts = { + type : 'counter', + counter : change + }; + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.counterSuccess, + mobile.ble.onError); +}; + +mobile.counterSuccess = function () { + console.log('Set counter'); + mobile.getState(); +}; + +function bytesToString (buffer) { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); +}; +function stringToBytes(string) { + var array = new Uint8Array(string.length); + for (var i = 0, l = string.length; i < l; i++) { + array[i] = string.charCodeAt(i); + } + return array.buffer; +}; \ No newline at end of file diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index c1851d4..66b9850 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -55,36 +55,12 @@ web.getState = function () { .then(res => { return res.json(); }) - .then(web.getStateSuccess) + .then(setState) .catch(err => { console.error('Error getting state'); console.error(err); }); }; -web.getStateSuccess = function (res) { - let exposure; - let scale; - if (res.frame.dir !== true) { - document.getElementById('dir').checked = true; - STATE.dir = res.frame.dir; - setDirLabel(false); - } - document.getElementById('counter').value = res.counter; - STATE.counter = res.counter; - //Exposure - if (res.frame.exposure === 0) { - res.frame.exposure = BOLEX.expected; - } - STATE.exposure = res.frame.exposure; - exposure = shutter(STATE.exposure); - scale = scaleAuto(STATE.exposure); - document.getElementById('str').value = exposure.str; - document.getElementById('scale').value = scale; - setExposureScale(); - - document.getElementById('delay').value = res.frame.delay; - STATE.delay = res.frame.delay; -}; web.setExposure = function () { let exposure = document.getElementById('exposure').value; let scaledExposure; @@ -165,6 +141,7 @@ web.init = function () { window.frame = web.frame; window.getState = web.getState; window.setDir = web.setDir; + window.setDelay = web.setDelay; window.setExposure = web.setExposure; window.setCounter = web.setCounter; console.log('started web') diff --git a/app/www/static/js/spin.min.js b/app/www/static/js/spin.min.js new file mode 100644 index 0000000..bd3ae4f --- /dev/null +++ b/app/www/static/js/spin.min.js @@ -0,0 +1,2 @@ +// http://spin.js.org/#v2.3.2 +!function(a,b){"object"==typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return m[e]||(k.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",k.cssRules.length),m[e]=1),e}function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice(1),void 0!==e[b])return b;for(d=0;d',c)}k.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.scale*d.width,left:d.scale*d.radius,top:-d.scale*d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.scale*(d.length+d.width),k=2*d.scale*j,l=-(d.width+d.length)*d.scale*2+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k Date: Wed, 13 Dec 2017 18:22:32 -0500 Subject: [PATCH 257/414] Name and advertise properly --- lib/ble/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index e5406a0..1bd4f44 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -11,7 +11,7 @@ const log = require('../log')('ble') const wifi = require('../wifi') const BLENO_DEVICE_NAME = process.env.BLENO_DEVICE_NAME || 'intval3' -const DEVICE_ID = process.env.DEVICE_ID || 'intval3' +let DEVICE_ID = process.env.DEVICE_ID || 'intval3' const SERVICE_ID = process.env.SERVICE_ID || 'intval3_ble' const CHAR_ID = process.env.CHAR_ID || 'intval3char' const WIFI_ID = process.env.WIFI_ID || 'wifichar' @@ -134,7 +134,7 @@ class BLE { log.info('stateChange', { state : state }) if (state === 'poweredOn') { log.info('Starting advertising', { BLE_ID : BLE_ID }) - bleno.startAdvertising(BLENO_DEVICE_NAME, [BLE_ID]) + bleno.startAdvertising(BLE_ID, [CHAR_ID, WIFI_ID]) } else { bleno.stopAdvertising() } From 567b1bb55d52ba6e8a5c662e605abb1826d4cd1e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 18:25:02 -0500 Subject: [PATCH 258/414] Advertisement length error, try with char_id, not both ids --- lib/ble/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 1bd4f44..1e8af08 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -134,7 +134,7 @@ class BLE { log.info('stateChange', { state : state }) if (state === 'poweredOn') { log.info('Starting advertising', { BLE_ID : BLE_ID }) - bleno.startAdvertising(BLE_ID, [CHAR_ID, WIFI_ID]) + bleno.startAdvertising(BLE_ID, [CHAR_ID]) } else { bleno.stopAdvertising() } From 6cabd0e9260e0e0e5c981e9255d46c2f9470cbe5 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 18:31:59 -0500 Subject: [PATCH 259/414] Use a better device id --- lib/ble/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 1e8af08..d9f2804 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -10,13 +10,14 @@ const util = require('util') const log = require('../log')('ble') const wifi = require('../wifi') -const BLENO_DEVICE_NAME = process.env.BLENO_DEVICE_NAME || 'intval3' -let DEVICE_ID = process.env.DEVICE_ID || 'intval3' +const DEVICE_NAME = process.env.DEVICE_ID || 'intval3' const SERVICE_ID = process.env.SERVICE_ID || 'intval3_ble' const CHAR_ID = process.env.CHAR_ID || 'intval3char' const WIFI_ID = process.env.WIFI_ID || 'wifichar' const NETWORK = os.networkInterfaces() const MAC = getMac() || spoofMac() +// +process.env.BLENO_DEVICE_NAME += '_' + MAC let currentWifi = 'disconnected' let getState @@ -130,11 +131,10 @@ class BLE { getState = bleGetState bleno.on('stateChange', state => { - const BLE_ID = `${DEVICE_ID}_${MAC}` log.info('stateChange', { state : state }) if (state === 'poweredOn') { - log.info('Starting advertising', { BLE_ID : BLE_ID }) - bleno.startAdvertising(BLE_ID, [CHAR_ID]) + log.info('Starting advertising', { DEVICE_NAME: DEVICE_NAME }) + bleno.startAdvertising(DEVICE_NAME, [CHAR_ID]) } else { bleno.stopAdvertising() } From 35feed7775351543c5e0189670a8c884e24aff72 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 18:34:00 -0500 Subject: [PATCH 260/414] Require bleno AFTER unique name is applied (this was messing up) --- lib/ble/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index d9f2804..c79a023 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -3,12 +3,7 @@ /** @module ble */ /** Bluetooth Low Energy module */ -const os = require('os') -const bleno = require('bleno') -const util = require('util') -const log = require('../log')('ble') -const wifi = require('../wifi') const DEVICE_NAME = process.env.DEVICE_ID || 'intval3' const SERVICE_ID = process.env.SERVICE_ID || 'intval3_ble' @@ -16,9 +11,16 @@ const CHAR_ID = process.env.CHAR_ID || 'intval3char' const WIFI_ID = process.env.WIFI_ID || 'wifichar' const NETWORK = os.networkInterfaces() const MAC = getMac() || spoofMac() -// + process.env.BLENO_DEVICE_NAME += '_' + MAC +const os = require('os') +const bleno = require('bleno') +const util = require('util') + +const log = require('../log')('ble') +const wifi = require('../wifi') + let currentWifi = 'disconnected' let getState From 57efee6d7f58f4023952b24329a7738b9803fd5e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 18:35:59 -0500 Subject: [PATCH 261/414] os module needs to be required first --- lib/ble/index.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index c79a023..c4812a3 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -3,7 +3,11 @@ /** @module ble */ /** Bluetooth Low Energy module */ +const util = require('util') +const os = require('os') +const log = require('../log')('ble') +const wifi = require('../wifi') const DEVICE_NAME = process.env.DEVICE_ID || 'intval3' const SERVICE_ID = process.env.SERVICE_ID || 'intval3_ble' @@ -13,13 +17,8 @@ const NETWORK = os.networkInterfaces() const MAC = getMac() || spoofMac() process.env.BLENO_DEVICE_NAME += '_' + MAC - -const os = require('os') const bleno = require('bleno') -const util = require('util') -const log = require('../log')('ble') -const wifi = require('../wifi') let currentWifi = 'disconnected' let getState @@ -135,7 +134,7 @@ class BLE { bleno.on('stateChange', state => { log.info('stateChange', { state : state }) if (state === 'poweredOn') { - log.info('Starting advertising', { DEVICE_NAME: DEVICE_NAME }) + log.info('Starting advertising', { DEVICE_NAME: DEVICE_NAME, DEVICE_ID : process.env.BLENO_DEVICE_NAME }) bleno.startAdvertising(DEVICE_NAME, [CHAR_ID]) } else { bleno.stopAdvertising() From 22d497b464416298de4d42b18d7822444c78b263 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 13 Dec 2017 18:40:14 -0500 Subject: [PATCH 262/414] BLE features are working, minus the wifi feature. Good work today. --- app/www/index.html | 3 +++ app/www/static/css/index.css | 12 +++++++++-- app/www/static/js/intval.mobile.js | 34 +++++++++++++++++++++++++----- index.js | 6 +++--- lib/ble/index.js | 3 ++- process.json | 2 +- 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 7298e13..86f1631 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -70,6 +70,8 @@ + +

    WIFI

    @@ -79,6 +81,7 @@
    +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index d64fc43..675d780 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -37,7 +37,7 @@ html,body,button,h2,label,input{ } body{ - padding-top: 15px; + padding-top: 35px; } .page{ padding: 5px 10% 0 10%; @@ -142,7 +142,7 @@ button{ border-radius: 5px; color: #fff; font-size: 18px; - background: transparent; + background: #363636; -webkit-appearance: none; box-shadow: none; outline: none; @@ -274,3 +274,11 @@ footer > div.selected{ #spinner{ margin-top: 200px; } + +#disconnect,#scan{ + display: none; +} +#disconnect.active, +#scan.active{ + display: block; +} diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 46669ce..6254471 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -19,15 +19,15 @@ mobile.ble.scan = function () { mobile.ble.devices = []; setTimeout(() => { if (!mobile.ble.connected) { - ble.stopScan(() => {}, mobile.ble.onError); spinnerHide(); alert('No INTVAL devices found.'); + settingsPage(); } }, 5000) }; mobile.ble.onDiscover = function (device) { - if (device && device.name && device.name === 'intval3') { + if (device && device.name && device.name.indexOf('intval3') !== -1) { console.log('BLE - Discovered INTVAL'); console.dir(device); mobile.ble.devices.push(device); @@ -47,19 +47,36 @@ mobile.ble.connect = function (device) { }; mobile.ble.onConnect = function (peripheral, device) { + const elem = document.getElementById('bluetooth') + const option = document.createElement('option') + const disconnect = document.getElementById('disconnect'); + const scan = document.getElementById('scan'); + spinnerHide(); - ble.stopScan(() => {}, moble.ble.onError); console.log(`BLE - Connected to ${device.id}`); console.log(peripheral); console.dir(device); + mobile.ble.device = device; mobile.ble.connected = true; - + + elem.innerHTML = ''; + option.text = device.name; + option.value = device.id; + elem.add(option); + + disconnect.classList.add('active'); + scan.classList.remove('active'); + getState(); }; mobile.ble.disconnect = function () { - let device + const elem = document.getElementById('bluetooth'); + const option = document.createElement('option'); + const disconnect = document.getElementById('disconnect'); + const scan = document.getElementById('scan'); + let device; if (!mobile.ble.connected) { console.warn('Not connected to any device') return false @@ -67,6 +84,13 @@ mobile.ble.disconnect = function () { device = mobile.ble.device console.log(`BLE - Disconnecting from ${device.id}`) ble.disconnect(device.id, mobile.ble.onDisconnect, mobile.ble.onDisconnect); + + elem.innerHTML = ''; + option.text = 'N/A'; + elem.add(option); + + disconnect.classList.remove('active'); + scan.classList.add('active'); }; mobile.ble.onDisconnect = function (res) { diff --git a/index.js b/index.js index c2159cb..193cd2c 100644 --- a/index.js +++ b/index.js @@ -314,10 +314,10 @@ function bFrame (obj, cb) { } } log.info('frame', { method : 'ble', dir : dir, exposure : exposure }) - /*intval.frame(dir, exposure, (len) => { + intval.frame(dir, exposure, (len) => { return cb() - })*/ - setTimeout(cb, exposure === 0 ? 630 : exposure) + }) + //setTimeout(cb, exposure === 0 ? 630 : exposure) } function bDir (obj, cb) { diff --git a/lib/ble/index.js b/lib/ble/index.js index c4812a3..4351dea 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -9,13 +9,14 @@ const os = require('os') const log = require('../log')('ble') const wifi = require('../wifi') -const DEVICE_NAME = process.env.DEVICE_ID || 'intval3' +const DEVICE_NAME = process.env.DEVICE_NAME || 'intval3' const SERVICE_ID = process.env.SERVICE_ID || 'intval3_ble' const CHAR_ID = process.env.CHAR_ID || 'intval3char' const WIFI_ID = process.env.WIFI_ID || 'wifichar' const NETWORK = os.networkInterfaces() const MAC = getMac() || spoofMac() +//Give the device a unique device name, needs to be in env process.env.BLENO_DEVICE_NAME += '_' + MAC const bleno = require('bleno') diff --git a/process.json b/process.json index f62bc31..c38a3d6 100644 --- a/process.json +++ b/process.json @@ -6,7 +6,7 @@ "watch" : false, "env" : { "BLENO_DEVICE_NAME" : "intval3", - "DEVICE_ID" : "intval3", + "DEVICE_NAME" : "intval3", "SERVICE_ID" : "149582bd-d49d-4b5c-acd1-1ae503d09e7a", "CHAR_ID" : "47bf69fb-f62f-4ef8-9be8-eb727a54fae4", "WIFI_ID" : "3fe7d9cf-7bd2-4ff0-97c5-ebe87288c2cc", From 2a2d71ec73ab5a31e43a2139a4cd58e85e6c5f8c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sun, 10 Dec 2017 16:11:14 -0500 Subject: [PATCH 263/414] Begin designing a carriage and stand for the intval. --- hardware/carriage.scad | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 hardware/carriage.scad diff --git a/hardware/carriage.scad b/hardware/carriage.scad new file mode 100644 index 0000000..ebf2c66 --- /dev/null +++ b/hardware/carriage.scad @@ -0,0 +1,10 @@ +THREADED_R = 12 / 2; +ROD_R = 13 / 2; + +module threaded_rod () { + $fn = 60; + length = 30 * 10; + cylinder(r = THREADED_R, h = length, center = true); +} + +translate([45, 0, 0]) rotate([90, 0, 0]) threaded_rod(); \ No newline at end of file From e6070465e5c552fd68eadbbff5223812e1cf4a13 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 18:40:51 -0500 Subject: [PATCH 264/414] Sequence logic ready for testing --- index.js | 86 ++++++++++++++++++++++++++++++++++++++++++- lib/sequence/index.js | 84 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 163 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 193cd2c..e2091fd 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,8 @@ function createBLE () { ble.on('exposure', bExposure) ble.on('delay', bDelay) ble.on('counter', bCounter) + ble.on('sequence', bSequence) + ble.on('reset', bReset) } //Restify functions @@ -246,6 +248,17 @@ function rSequence (req, res, next) { let dir = true let exposure = 0 let delay = 0 + + if (intval._state.frame.dir !== true) { + dir = false + } + if (intval._state.frame.exposure !== 0) { + exposure = intval._state.frame.exposure + } + if (intval._state.frame.delay !== 0) { + delay = intval._state.frame.delay + } + if (req.query && typeof req.query.dir !== 'undefined') { if (typeof req.query.dir === 'string') { dir = (req.query.dir === 'true') @@ -274,13 +287,24 @@ function rSequence (req, res, next) { exposure = req.body.exposure } } - if (sequence.active) { + if (sequence._state.active) { return sequence.stop(() => { res.send({ stopped : true }) return next() }) } else { - return sequence.start({}, (seq) => { + return sequence.start({ + loop : [ (next) => { + intval.frame(dir, exposure, (len) => { + next() + }), + }, (next) => { + setTimeout(() => { + next() + }, delay) + } + ] + }, (seq) => { res.send(seq) return next() }) @@ -379,6 +403,64 @@ function bCounter (obj, cb) { return cb() } +function bSequence (obj, cb) { + let dir = true + let exposure = 0 + let delay = 0 + + if (intval._state.frame.dir !== true) { + dir = false + } + if (intval._state.frame.exposure !== 0) { + exposure = intval._state.frame.exposure + } + if (intval._state.frame.delay !== 0) { + delay = intval._state.frame.delay + } + + if (typeof obj.dir !== 'undefined') { + if (typeof obj.dir === 'string') { + dir = (obj.dir === 'true') + } else { + dir = obj.dir + } + } + if (typeof obj.exposure !== 'undefined') { + if (typeof obj.exposure === 'string') { + exposure = parseInt(obj.exposure) + } else { + exposure = obj.exposure + } + } + if (sequence._state.active) { + return sequence.stop(() => { + return cb() + }) + } else { + console.time('sequence time') + sequence.start({ + loop : [ (next) => { + intval.frame(dir, exposure, (len) => { + next() + }), + }, (next) => { + setTimeout(() => { + next() + }, delay) + } + ] + }, (seq) => { + console.timeEnd('sequence time') + }) + return cb() + } +} + +function bReset (obj, cb) { + intval.reset() + setTimeout(cb, 10) +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 98d371a..10849a4 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -1,17 +1,91 @@ 'use strict' -const sequence = { +const log = require('../log')('seq') + +/** Object sequence features */ +const sequence = {}; + +sequence._state = { + arr : [], active : false, + paused : false, frame: false, - delay : false + delay : false, + count : 0, + stop : null +} + +sequence._loop = { + arr : [], + count : 0, + max : 0 } sequence.start = function (options, cb) { - sequence.active = true + sequence._state.active = true + sequence._state.count = 0 + + if (options.arr) { + sequence._state.arr = options.arr + } + + if (options.loop) { + sequence._loop.arr = options.loop + sequence._loop.count = 0 + } + + if (options.maxLoop) { + sequence._loop.max = options.maxLoop + } else { + sequence._loop.max = 0 + } + + sequence.step() } -sequence.stop = function (cb) { - sequence.active = false +sequence.stop = function (next) { + sequence._state.active = false + sequence._state.count = 0 + sequence._loop.count = 0 + sequence._state.stop() + next() +} + +sequence.pause = function () { + sequence._state.paused = true +} + +sequence.resume = function () { + sequence._state.paused = false + sequence.step() +} + +sequence.step = function () { + if (sequence._state.active && !sequence._state.paused) { + if (sequence._state.arr.length > 0 && typeof sequence._state.arr[sequence._state.count] !== 'undefined') { + return sequence._state.arr[sequence._state.count](() => { + sequence._state.count++ + sequence.step() + }) + } else if (sequence._loop.arr.length > 0 && typeof sequence._loop.arr[sequence._state.count] !== 'undefined') { + if (sequence._state.count > sequence._loop.arr.length) { + sequence._state.count = 0 + sequence._loop.count++ + } + if (sequence._loop.count > sequence._loop.max) { + return sequence.stop() + } + return sequence._loop.arr[sequence._state.count](() => { + sequence._state.count++ + }) + } else{ + return sequence.stop() + } + } else if (sequence._state.paused) { + log.info('Sequence paused', { loop : sequence._loop.count, count : sequence._state.count }) + } else if (!sequence._state.active) { + log.info('Sequence stopped', { loop : sequence._loop.count, count : sequence._state.count }) + } } module.exports = sequence \ No newline at end of file From af28f14ecc3f0db66a2b3e5f12fb6cb8af88b1a7 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:42:20 -0500 Subject: [PATCH 265/414] Fix syntax error and update to latest node modules on ubuntu --- index.js | 10 +- package-lock.json | 647 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 642 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index e2091fd..fa20f90 100644 --- a/index.js +++ b/index.js @@ -297,13 +297,12 @@ function rSequence (req, res, next) { loop : [ (next) => { intval.frame(dir, exposure, (len) => { next() - }), + }) }, (next) => { setTimeout(() => { next() }, delay) - } - ] + }] }, (seq) => { res.send(seq) return next() @@ -442,13 +441,12 @@ function bSequence (obj, cb) { loop : [ (next) => { intval.frame(dir, exposure, (len) => { next() - }), + }) }, (next) => { setTimeout(() => { next() }, delay) - } - ] + }] }, (seq) => { console.timeEnd('sequence time') }) diff --git a/package-lock.json b/package-lock.json index 620c7ab..9926003 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, "acorn": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", @@ -19,6 +25,16 @@ "acorn": "3.3.0" } }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -34,6 +50,11 @@ "array-back": "1.0.4" } }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, "app-usage-stats": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/app-usage-stats/-/app-usage-stats-0.5.1.tgz", @@ -46,6 +67,22 @@ "usage-stats": "0.9.4" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, "array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", @@ -70,6 +107,24 @@ "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "optional": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "optional": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -94,17 +149,45 @@ "resolved": "https://registry.npmjs.org/bleno/-/bleno-0.4.2.tgz", "integrity": "sha1-IesK10O850eU45L0ph4TsHOT26o=", "requires": { + "bluetooth-hci-socket": "0.5.1", "bplist-parser": "0.0.6", "debug": "2.6.8", "xpc-connection": "0.1.4" } }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, "bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", "dev": true }, + "bluetooth-hci-socket": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.1.tgz", + "integrity": "sha1-774hUk/Bz10/rl1RNl1WHUq77Qs=", + "optional": true, + "requires": { + "debug": "2.6.8", + "nan": "2.6.2", + "usb": "1.3.1" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, "bplist-parser": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", @@ -153,6 +236,12 @@ } } }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true + }, "catharsis": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", @@ -171,6 +260,17 @@ "is-supported-regexp-flag": "1.0.0" } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, "collect-all": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.3.tgz", @@ -186,6 +286,14 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, "command-line-args": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-4.0.7.tgz", @@ -273,6 +381,11 @@ "walk-back": "2.0.1" } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -286,6 +399,14 @@ "moment-timezone": "0.5.13" } }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, "csv": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", @@ -348,6 +469,23 @@ "integrity": "sha1-HKb/7dvO8XFd16riXHYW+a4iky8=", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true + }, "detect-node": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", @@ -439,6 +577,12 @@ "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "optional": true + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -485,6 +629,23 @@ "test-value": "2.1.0" } }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, "formidable": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", @@ -499,8 +660,45 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.4.5" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } }, "getpass": { "version": "0.1.7", @@ -514,7 +712,6 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "optional": true, "requires": { "inflight": "1.0.6", "inherits": "2.0.3", @@ -531,9 +728,7 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "optional": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "handle-thing": { "version": "1.2.5", @@ -551,6 +746,44 @@ "uglify-js": "2.3.6" } }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, "home-path": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.5.tgz", @@ -597,6 +830,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "optional": true + }, "is-absolute": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", @@ -606,6 +845,14 @@ "is-windows": "0.2.0" } }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -624,6 +871,12 @@ "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz", "integrity": "sha1-i1IMhfrnolM4LUsCZS4EVXbhO7g=" }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true + }, "is-unc-path": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", @@ -753,6 +1006,27 @@ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "optional": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -821,6 +1095,19 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -918,6 +1205,89 @@ "q": "1.1.2" } }, + "node-pre-gyp": { + "version": "0.6.39", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.2", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.4.1", + "tar": "2.2.1", + "tar-pack": "3.4.1" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "optional": true, + "requires": { + "glob": "7.1.2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, "object-get": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.0.tgz", @@ -961,11 +1331,39 @@ "wordwrap": "0.0.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "optional": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "optional": true + }, "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", @@ -976,6 +1374,12 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "optional": true + }, "q": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz", @@ -986,6 +1390,32 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" }, + "rc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", + "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "optional": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "optional": true + } + } + }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", @@ -1066,6 +1496,61 @@ } } }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + }, + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "optional": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "optional": true + } + } + }, "requizzle": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", @@ -1125,7 +1610,6 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", - "optional": true, "requires": { "glob": "6.0.4" } @@ -1151,6 +1635,26 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "optional": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, "sort-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-1.1.2.tgz", @@ -1966,6 +2470,16 @@ "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", @@ -1974,11 +2488,24 @@ "safe-buffer": "5.1.1" } }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "table-layout": { "version": "0.4.2", @@ -2010,6 +2537,57 @@ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", "dev": true }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "optional": true, + "requires": { + "glob": "7.1.2" + } + } + } + }, "temp-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", @@ -2026,6 +2604,24 @@ "typical": "2.6.1" } }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -2069,6 +2665,12 @@ } } }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "optional": true + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -2122,6 +2724,24 @@ } } }, + "usb": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.3.1.tgz", + "integrity": "sha1-tfjDYKU78o9cn7wS1kx/YeQ0arc=", + "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "optional": true + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2179,6 +2799,15 @@ "minimalistic-assert": "1.0.0" } }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, "winston": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/winston/-/winston-2.3.1.tgz", From 0a3add86cb7d14c7de7ca66fdb712cb8e7f4da0d Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:46:53 -0500 Subject: [PATCH 266/414] Only log stop function --- lib/sequence/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 10849a4..3f196d5 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -47,7 +47,7 @@ sequence.stop = function (next) { sequence._state.active = false sequence._state.count = 0 sequence._loop.count = 0 - sequence._state.stop() + console.dir(sequence._state.stop) next() } From fc99b9ff06ea150721c2f4dd0286c569060e506c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:51:14 -0500 Subject: [PATCH 267/414] Do not wait for sequence to finish to respond to /sequence request --- index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index fa20f90..c580d94 100644 --- a/index.js +++ b/index.js @@ -293,6 +293,7 @@ function rSequence (req, res, next) { return next() }) } else { + console.time('sequence time') return sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { @@ -304,9 +305,10 @@ function rSequence (req, res, next) { }, delay) }] }, (seq) => { - res.send(seq) - return next() + console.timeEnd('sequence time') }) + res.send({}) + return next() } } From 573287384d36fb4f2e8df8110e117a6c63120090 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:53:21 -0500 Subject: [PATCH 268/414] Actually call next step when running a loop --- lib/sequence/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 3f196d5..a134ea3 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -77,6 +77,7 @@ sequence.step = function () { } return sequence._loop.arr[sequence._state.count](() => { sequence._state.count++ + sequence.step() }) } else{ return sequence.stop() From 0668d856beddc85a49f4a35284bfd710cc2a4e95 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:57:02 -0500 Subject: [PATCH 269/414] Errored out trying a stop function --- lib/sequence/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index a134ea3..e071336 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -48,7 +48,7 @@ sequence.stop = function (next) { sequence._state.count = 0 sequence._loop.count = 0 console.dir(sequence._state.stop) - next() + if (next) next() } sequence.pause = function () { From dbf641ef6909d4ac47bca081d9df4ca4b5eebed3 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 20:59:52 -0500 Subject: [PATCH 270/414] See how sequence.stop is being called, using console.trace --- lib/sequence/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index e071336..6c2a8ff 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -48,6 +48,7 @@ sequence.stop = function (next) { sequence._state.count = 0 sequence._loop.count = 0 console.dir(sequence._state.stop) + console.trace() if (next) next() } From 66ac78fee0cca2d9cd3130a2b3bc501f1d3c3d54 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:03:57 -0500 Subject: [PATCH 271/414] If state is malformed, quit and restart app --- lib/intval/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index 04b3831..df40ad7 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -59,6 +59,9 @@ intval.init = function () { }) }).catch((err) => { log.error('init', err) + fs.rmdirSync('./state') + log.warn('Restarting') + process.exit() }) intval._frame = { From c7ed8bed3b0a64134d9fd7f115a4cb49bdd8c306 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:12:40 -0500 Subject: [PATCH 272/414] Was returning sequence start, now calling and then returning --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index c580d94..459979a 100644 --- a/index.js +++ b/index.js @@ -294,7 +294,7 @@ function rSequence (req, res, next) { }) } else { console.time('sequence time') - return sequence.start({ + sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { next() From 64b024497288fff4b911cb82011d46c5b94bbbde Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:24:51 -0500 Subject: [PATCH 273/414] Store callback to invoke at the end of a sequence --- lib/sequence/index.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 6c2a8ff..b1af7a6 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -39,17 +39,20 @@ sequence.start = function (options, cb) { } else { sequence._loop.max = 0 } - + sequence._state.stop = cb sequence.step() } -sequence.stop = function (next) { +sequence.setStop = function () { + sequence._state.active = false +} + +sequence.stop = function () { sequence._state.active = false sequence._state.count = 0 sequence._loop.count = 0 - console.dir(sequence._state.stop) - console.trace() - if (next) next() + if (sequence._state.stop) sequence._state.stop() + sequence._state.stop = null } sequence.pause = function () { @@ -63,12 +66,15 @@ sequence.resume = function () { sequence.step = function () { if (sequence._state.active && !sequence._state.paused) { - if (sequence._state.arr.length > 0 && typeof sequence._state.arr[sequence._state.count] !== 'undefined') { + if (sequence._state.arr.length > 0) { + if (sequence._state.count > sequence._state.arr.length) { + return sequence.stop() + } return sequence._state.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() }) - } else if (sequence._loop.arr.length > 0 && typeof sequence._loop.arr[sequence._state.count] !== 'undefined') { + } else if (sequence._loop.arr.length > 0) { if (sequence._state.count > sequence._loop.arr.length) { sequence._state.count = 0 sequence._loop.count++ From a039e8f61672b505905bf9fb4ad255707b8bd4e8 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:26:59 -0500 Subject: [PATCH 274/414] Do NOT use maximum loop logic when steppign throuhg a sequence without a max loop --- lib/sequence/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index b1af7a6..0fe5de9 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -50,7 +50,12 @@ sequence.setStop = function () { sequence.stop = function () { sequence._state.active = false sequence._state.count = 0 + sequence._state.arr = [] + sequence._loop.count = 0 + sequence._loop.max = 0 + sequence._loop.arr = [] + if (sequence._state.stop) sequence._state.stop() sequence._state.stop = null } @@ -79,7 +84,7 @@ sequence.step = function () { sequence._state.count = 0 sequence._loop.count++ } - if (sequence._loop.count > sequence._loop.max) { + if (sequence._loop.max > 0 &&sequence._loop.count > sequence._loop.max) { return sequence.stop() } return sequence._loop.arr[sequence._state.count](() => { From b605f875bcf3f613f29af1559fbf447a121d160a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:32:07 -0500 Subject: [PATCH 275/414] Declare intval object variables before init function --- lib/intval/index.js | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index df40ad7..85a5972 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -38,6 +38,19 @@ const PINS = { /** Object representing the intval3 features */ const intval = {} +intval._frame = { + open : 250, //delay before pausing frame in open state + openBwd : 400, + closed : 100, //time that frame actually remains closed for + expected : 630 //expected length of frame, in ms +} +intval._release = { + min : 20, + seq : 1000 +} +intval._microDelay = 10 // delay after stop signal before stopping motors +intval._pin = {} + intval.init = function () { if (!fs.existsSync('./state')) fs.mkdirSync('./state') storage.init({ @@ -51,37 +64,26 @@ intval.init = function () { ttl: false, // ttl* [NEW], can be true for 24h default or a number in MILLISECONDS expiredInterval: 2 * 60 * 1000, // [NEW] every 2 minutes the process will clean-up the expired cache forgiveParseErrors: false // [NEW] - }).then((res) => { - //console.dir(res) - storage.getItem('_state', 'test').then(intval._setState).catch((err) => { - intval._setState(undefined) - log.error('init', err) - }) - }).catch((err) => { + }).then(intval._restoreState).catch((err) => { log.error('init', err) fs.rmdirSync('./state') log.warn('Restarting') - process.exit() + return process.exit(1) }) - intval._frame = { - open : 250, //delay before pausing frame in open state - openBwd : 400, - closed : 100, //time that frame actually remains closed for - expected : 630 //expected length of frame, in ms - } - intval._release = { - min : 20, - seq : 1000 - } - intval._microDelay = 10 // delay after stop signal before stopping motors - intval._pin = {} - - intval._declarePins() process.on('SIGINT', intval._undeclarePins) process.on('uncaughtException', intval._undeclarePins) } +intval._restoreState = function (res) { + //console.dir(res) + storage.getItem('_state', 'test').then(intval._setState).catch((err) => { + intval._setState(undefined) + log.error('init', err) + }) + intval._declarePins() +} + intval._setState = function (data) { if (typeof data !== 'undefined') { intval._state = data From ec156fc8b6d65adf6facc2b3d05a1f4db6471222 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:34:00 -0500 Subject: [PATCH 276/414] Log output of removing state directory --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 85a5972..5687266 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -66,7 +66,7 @@ intval.init = function () { forgiveParseErrors: false // [NEW] }).then(intval._restoreState).catch((err) => { log.error('init', err) - fs.rmdirSync('./state') + console.dir(fs.rmdirSync('./state')) log.warn('Restarting') return process.exit(1) }) From 64440f7c1c81c49af451707429f2368fb6164b91 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:39:17 -0500 Subject: [PATCH 277/414] Set state if invalid. Hopefully it overwrites? --- lib/intval/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 5687266..fd43121 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -66,9 +66,7 @@ intval.init = function () { forgiveParseErrors: false // [NEW] }).then(intval._restoreState).catch((err) => { log.error('init', err) - console.dir(fs.rmdirSync('./state')) - log.warn('Restarting') - return process.exit(1) + return intval._setState() }) process.on('SIGINT', intval._undeclarePins) @@ -79,7 +77,7 @@ intval._restoreState = function (res) { //console.dir(res) storage.getItem('_state', 'test').then(intval._setState).catch((err) => { intval._setState(undefined) - log.error('init', err) + log.error('_restoreState', err) }) intval._declarePins() } @@ -91,6 +89,7 @@ intval._setState = function (data) { log.info('_setState', 'Restored intval state from disk') return true } + log.info('_setState', 'Setting state from defaults') intval._state = { frame : { dir : true, //forward From b1085cb77c58566cab89dc40596f78157d4ba887 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:41:41 -0500 Subject: [PATCH 278/414] Need to declare pins even during state restore failure. --- lib/intval/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index fd43121..6e2ea37 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -65,8 +65,9 @@ intval.init = function () { expiredInterval: 2 * 60 * 1000, // [NEW] every 2 minutes the process will clean-up the expired cache forgiveParseErrors: false // [NEW] }).then(intval._restoreState).catch((err) => { - log.error('init', err) - return intval._setState() + log.warn('init', err) + intval._setState() + intval._declarePins() }) process.on('SIGINT', intval._undeclarePins) From 11bf8b95f895bbd280301e4edfcfe63d7b0689dc Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:49:26 -0500 Subject: [PATCH 279/414] flag the start of a sequence and stop it. --- index.js | 12 ++++++++---- lib/sequence/index.js | 13 +++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 459979a..78f6624 100644 --- a/index.js +++ b/index.js @@ -287,13 +287,15 @@ function rSequence (req, res, next) { exposure = req.body.exposure } } - if (sequence._state.active) { - return sequence.stop(() => { + if (intval._state.sequence && sequence._state.active) { + return sequence.setStop(() => { + intval._state.sequence = false res.send({ stopped : true }) return next() }) } else { console.time('sequence time') + intval._state.sequence = true sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { @@ -433,12 +435,14 @@ function bSequence (obj, cb) { exposure = obj.exposure } } - if (sequence._state.active) { - return sequence.stop(() => { + if (intval._state.sequence && sequence._state.active) { + return sequence.setStop(() => { + intval._state.sequence = false return cb() }) } else { console.time('sequence time') + intval._state.sequence = true sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 0fe5de9..857fb58 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -43,8 +43,9 @@ sequence.start = function (options, cb) { sequence.step() } -sequence.setStop = function () { +sequence.setStop = function (cb) { sequence._state.active = false + sequence._state.input = cb } sequence.stop = function () { @@ -57,7 +58,9 @@ sequence.stop = function () { sequence._loop.arr = [] if (sequence._state.stop) sequence._state.stop() + if (sequence._state.input) sequence._state.input() sequence._state.stop = null + sequence._state.input = null } sequence.pause = function () { @@ -75,6 +78,7 @@ sequence.step = function () { if (sequence._state.count > sequence._state.arr.length) { return sequence.stop() } + log.info('step') return sequence._state.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() @@ -84,9 +88,10 @@ sequence.step = function () { sequence._state.count = 0 sequence._loop.count++ } - if (sequence._loop.max > 0 &&sequence._loop.count > sequence._loop.max) { + if (sequence._loop.max > 0 && sequence._loop.count > sequence._loop.max) { return sequence.stop() } + log.info() return sequence._loop.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() @@ -95,9 +100,9 @@ sequence.step = function () { return sequence.stop() } } else if (sequence._state.paused) { - log.info('Sequence paused', { loop : sequence._loop.count, count : sequence._state.count }) + log.info('step', 'Sequence paused', { loop : sequence._loop.count, count : sequence._state.count }) } else if (!sequence._state.active) { - log.info('Sequence stopped', { loop : sequence._loop.count, count : sequence._state.count }) + log.info('step', 'Sequence stopped', { loop : sequence._loop.count, count : sequence._state.count }) } } From 19815dbb4df57d705c5bf8b6bd60accdc3de1e8b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 21:59:30 -0500 Subject: [PATCH 280/414] Log step sequence to keep track during tests. OFF BY ONE! OFF BY ONE! --- lib/sequence/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 857fb58..2461aa7 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -59,6 +59,7 @@ sequence.stop = function () { if (sequence._state.stop) sequence._state.stop() if (sequence._state.input) sequence._state.input() + sequence._state.stop = null sequence._state.input = null } @@ -73,25 +74,26 @@ sequence.resume = function () { } sequence.step = function () { + log.info('step', `Started step ${sequence._state.count}`) if (sequence._state.active && !sequence._state.paused) { if (sequence._state.arr.length > 0) { - if (sequence._state.count > sequence._state.arr.length) { + if (sequence._state.count > sequence._state.arr.length - 1) { return sequence.stop() } - log.info('step') + log.info('step', sequence._state.count) return sequence._state.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() }) } else if (sequence._loop.arr.length > 0) { - if (sequence._state.count > sequence._loop.arr.length) { + if (sequence._state.count > sequence._loop.arr.length - 1) { sequence._state.count = 0 sequence._loop.count++ } if (sequence._loop.max > 0 && sequence._loop.count > sequence._loop.max) { return sequence.stop() } - log.info() + log.info('step', sequence._state.count) return sequence._loop.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() From a7bcb0a87bae76c453ab0f29686d452453795bc4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:10:36 -0500 Subject: [PATCH 281/414] Combine sequence and intval functionality --- index.js | 40 ++++++++++++++++++++++++++++++++++++++++ lib/intval/index.js | 8 +------- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 78f6624..5fbf35b 100644 --- a/index.js +++ b/index.js @@ -287,6 +287,20 @@ function rSequence (req, res, next) { exposure = req.body.exposure } } + if (req.query && typeof req.query.delay !== 'undefined') { + if (typeof req.query.delay === 'string') { + delay = parseInt(req.query.delay) + } else { + delay = req.query.delay + } + } + if (req.body && typeof req.body.delay!== 'undefined') { + if (typeof req.body.delay === 'string') { + delay = parseInt(req.body.delay) + } else { + delay = req.body.delay + } + } if (intval._state.sequence && sequence._state.active) { return sequence.setStop(() => { intval._state.sequence = false @@ -465,6 +479,31 @@ function bReset (obj, cb) { setTimeout(cb, 10) } +function seq () { + if (intval._state.sequence && sequence._state.active) { + return sequence.setStop(() => { + intval._state.sequence = false + return cb() + }) + } else { + console.time('sequence time') + intval._state.sequence = true + sequence.start({ + loop : [ (next) => { + intval.frame(dir, exposure, (len) => { + next() + }) + }, (next) => { + setTimeout(() => { + next() + }, delay) + }] + }, (seq) => { + console.timeEnd('sequence time') + }) + } +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { @@ -477,6 +516,7 @@ function index (req, res, next) { function init () { intval.init() + intval.sequence = seq createServer() createBLE() } diff --git a/lib/intval/index.js b/lib/intval/index.js index 6e2ea37..2d53e14 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -412,13 +412,7 @@ intval.frame = function (dir = null, exposure = null, cb = () => {}) { exposure: exposure } } -/** -* Start a sequence of frames, using defaults or explicit instructions -* -*/ -intval.sequence = function () { - log.info('sequence', `Started sequence`) -} + intval.status = function () { return intval._state } From 386de6dc561cca211f38968e48803bbc911c39f7 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:15:40 -0500 Subject: [PATCH 282/414] Add delay restify endpoint. --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 5fbf35b..f96cf84 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,8 @@ function createServer () { app.post('/dir', rDir) app.get( '/exposure', rExposure) app.post('/exposure', rExposure) + app.get( '/delay', rDelay) + app.post('/delay', rDelay) app.get( '/counter', rCounter) app.post('/counter', rCounter) app.get( '/frame', rFrame) From c36c0ab598c4a37cdbfcc9f4e29011f80fac27a4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:30:13 -0500 Subject: [PATCH 283/414] Declare variables to pass into sequence.start() --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index f96cf84..0277b61 100644 --- a/index.js +++ b/index.js @@ -482,6 +482,10 @@ function bReset (obj, cb) { } function seq () { + let dir = intval._state.frame.dir + let exposure = intval._state.frame.exposure + let delay = intval._state.frame.delay + if (intval._state.sequence && sequence._state.active) { return sequence.setStop(() => { intval._state.sequence = false From 0b48dc87b458d39d364453f0762e62cc9a663b07 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:30:48 -0500 Subject: [PATCH 284/414] use the microdelay to prevent multiple frames --- lib/intval/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 2d53e14..287fd3c 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -245,9 +245,9 @@ intval._watchMicro = function (err, val) { if (intval._state.micro.primed && !intval._state.micro.paused && (now - intval._state.frame.start) > intval._frame.open) { intval._state.micro.primed = false intval._state.micro.time = 0 - //setTimeout( () => { + setTimeout( () => { intval._stop() - //}, intval._microDelay) + }, intval._microDelay) } } } From 02aea27ff95e3dd9e6bae6a1d1ef538781330ab2 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:34:30 -0500 Subject: [PATCH 285/414] Log when sequences are started and stopped by release cable --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 0277b61..fbfdd33 100644 --- a/index.js +++ b/index.js @@ -485,14 +485,16 @@ function seq () { let dir = intval._state.frame.dir let exposure = intval._state.frame.exposure let delay = intval._state.frame.delay - + if (intval._state.sequence && sequence._state.active) { + log.info('sequence', { method : 'release' , stop: true }) return sequence.setStop(() => { intval._state.sequence = false return cb() }) } else { console.time('sequence time') + log.info('sequence', { method : 'release', start : true }) intval._state.sequence = true sequence.start({ loop : [ (next) => { From 8333f451f876ab7402003f43ddf7617531eda4d3 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 22:39:20 -0500 Subject: [PATCH 286/414] Post sequence callbacks are not firing. Skip for now and just call back immediately --- index.js | 16 +++++++--------- lib/sequence/index.js | 6 +----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index fbfdd33..4f34023 100644 --- a/index.js +++ b/index.js @@ -304,11 +304,10 @@ function rSequence (req, res, next) { } } if (intval._state.sequence && sequence._state.active) { - return sequence.setStop(() => { - intval._state.sequence = false - res.send({ stopped : true }) - return next() - }) + sequence.setStop() + intval._state.sequence = false + res.send({ stopped : true }) + return next() } else { console.time('sequence time') intval._state.sequence = true @@ -488,10 +487,9 @@ function seq () { if (intval._state.sequence && sequence._state.active) { log.info('sequence', { method : 'release' , stop: true }) - return sequence.setStop(() => { - intval._state.sequence = false - return cb() - }) + sequence.setStop() + intval._state.sequence = false + return cb() } else { console.time('sequence time') log.info('sequence', { method : 'release', start : true }) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index 2461aa7..d785ad9 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -43,9 +43,8 @@ sequence.start = function (options, cb) { sequence.step() } -sequence.setStop = function (cb) { +sequence.setStop = function () { sequence._state.active = false - sequence._state.input = cb } sequence.stop = function () { @@ -58,10 +57,8 @@ sequence.stop = function () { sequence._loop.arr = [] if (sequence._state.stop) sequence._state.stop() - if (sequence._state.input) sequence._state.input() sequence._state.stop = null - sequence._state.input = null } sequence.pause = function () { @@ -74,7 +71,6 @@ sequence.resume = function () { } sequence.step = function () { - log.info('step', `Started step ${sequence._state.count}`) if (sequence._state.active && !sequence._state.paused) { if (sequence._state.arr.length > 0) { if (sequence._state.count > sequence._state.arr.length - 1) { From e1a54c10e0a955aded452dc4a7b2cd876e4dee53 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:29:08 -0500 Subject: [PATCH 287/414] Prevent sequence from starting if already started. Add an id to the sequence, for logging (though the doubling up should discontinue). --- lib/sequence/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/sequence/index.js b/lib/sequence/index.js index d785ad9..f60d86e 100644 --- a/lib/sequence/index.js +++ b/lib/sequence/index.js @@ -1,5 +1,6 @@ 'use strict' +const uuid = require('uuid').v4 const log = require('../log')('seq') /** Object sequence features */ @@ -22,6 +23,10 @@ sequence._loop = { } sequence.start = function (options, cb) { + if (sequence._state.active) { + return false + } + sequence._state.active = true sequence._state.count = 0 @@ -41,6 +46,8 @@ sequence.start = function (options, cb) { } sequence._state.stop = cb sequence.step() + sequence._state.id = uuid() + return sequence._state.id } sequence.setStop = function () { @@ -76,7 +83,7 @@ sequence.step = function () { if (sequence._state.count > sequence._state.arr.length - 1) { return sequence.stop() } - log.info('step', sequence._state.count) + log.info('step', { count : sequence._state.count, id : sequence._state.id }) return sequence._state.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() @@ -89,7 +96,7 @@ sequence.step = function () { if (sequence._loop.max > 0 && sequence._loop.count > sequence._loop.max) { return sequence.stop() } - log.info('step', sequence._state.count) + log.info('step', { count : sequence._state.count, id : sequence._state.id }) return sequence._loop.arr[sequence._state.count](() => { sequence._state.count++ sequence.step() From ccd87b4d7ea3005d7293244a4f762c297d333198 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:29:43 -0500 Subject: [PATCH 288/414] Send a "started" object when sequence is started --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 4f34023..40a50e3 100644 --- a/index.js +++ b/index.js @@ -324,7 +324,7 @@ function rSequence (req, res, next) { }, (seq) => { console.timeEnd('sequence time') }) - res.send({}) + res.send({ started : true }) return next() } } From e880e44a5c3ea9346187e8b95092fed368b00e91 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:47:37 -0500 Subject: [PATCH 289/414] Control sequence with GUIs --- app/www/static/css/index.css | 3 ++- app/www/static/js/intval.core.js | 9 +++++++- app/www/static/js/intval.mobile.js | 34 ++++++++++++++++++++++++++++++ app/www/static/js/intval.web.js | 21 ++++++++++++++++++ index.js | 16 +++++++++++--- 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 675d780..b7e093b 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -193,7 +193,7 @@ button.focus{ } #frame{ - padding: 10px 0; + padding: 20px 0; font-size: 24px; } @@ -254,6 +254,7 @@ footer > div.selected{ #seq{ margin-top: 40px; + padding: 10px 0; } #overlay{ diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 81dee9c..f0c768d 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -10,7 +10,8 @@ const STATE = { delay : 0, scale : 'ms', delayScale : 'ms', - counter : 0 + counter : 0, + sequence : false }; //functions @@ -160,6 +161,12 @@ var setState = function (res) { exposure = shutter(STATE.exposure); exposureScale = scaleAuto(STATE.exposure); + if (res.sequence === true) { + STATE.sequence = true; + mobile.ble.active = true; + document.getElementById('seq').classList.add('focus'); + } + document.getElementById('str').value = exposure.str; document.getElementById('scale').value = exposureScale; setExposureScale(); diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 6254471..fbe2f92 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -245,6 +245,40 @@ mobile.counterSuccess = function () { mobile.getState(); }; +mobile.sequence = function () { + const opts = { + type : 'sequence' + }; + if (!mobile.ble.connected) { + return alert('Not connected to an INTVAL device.'); + } + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.frameSuccess, + mobile.ble.onError); + document.getElementById('seq').classList.add('focus'); + mobile.ble.active = true; +}; + + +mobile.sequenceSuccess = function () { + console.log('Sequence state changed'); + mobile.getState(); + setTimeout(() => { + if (STATE.sequence) { + mobile.ble.active = true; + if (!document.getElementById('seq').classList.contains('focus')) { + document.getElementById('seq').classList.add('focus'); + } + } else { + mobile.ble.active = false; + document.getElementById('seq').classList.remove('focus'); + } + }, 20); +} + function bytesToString (buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); }; diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 66b9850..6bff2b1 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -134,6 +134,27 @@ web.setCounterSuccess = function (res) { forceCounter(res.counter); console.log(`setCounter to ${res.counter}`); }; +web.sequence = function () { + const opts = { + method : 'POST', + headers : web._header, + body : JSON.stringify({}) + } + fetch('/sequence', opts) + .then(web.useJson) + .then(web.sequenceSuccess) + .catch(err => { + console.error('Error getting /sequence'); + console.error(err); + }) +} +web.sequenceSuccess = function (res) { + if (res.started && res.started != false) { + document.getElementById('seq').focus(); + } else if (res.stopped) { + document.getElementById('seq').blur(); + } +} web.useJson = function (res) { return res.json(); } diff --git a/index.js b/index.js index 40a50e3..8c3417e 100644 --- a/index.js +++ b/index.js @@ -311,7 +311,7 @@ function rSequence (req, res, next) { } else { console.time('sequence time') intval._state.sequence = true - sequence.start({ + let seq_id = sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { next() @@ -324,7 +324,13 @@ function rSequence (req, res, next) { }, (seq) => { console.timeEnd('sequence time') }) - res.send({ started : true }) + + if (seq_id === false) { + res.send({ started : false }) + } else { + res.send({ started : true , id : seq_id }) + } + return next() } } @@ -453,12 +459,13 @@ function bSequence (obj, cb) { if (intval._state.sequence && sequence._state.active) { return sequence.setStop(() => { intval._state.sequence = false + log.info('sequence stop'. { method : 'ble', id : seq_id }) return cb() }) } else { console.time('sequence time') intval._state.sequence = true - sequence.start({ + let seq_id = sequence.start({ loop : [ (next) => { intval.frame(dir, exposure, (len) => { next() @@ -471,6 +478,9 @@ function bSequence (obj, cb) { }, (seq) => { console.timeEnd('sequence time') }) + if (seq_id !== false) { + log.info('sequence start'. { method : 'ble', id : seq_id }) + } return cb() } } From 2545df2c3873177aa2d7dab1af695ba8dd41d931 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:49:51 -0500 Subject: [PATCH 290/414] Add events to the html and respective libraries, web, mobile and core --- app/www/index.html | 2 +- app/www/static/js/intval.core.js | 1 + app/www/static/js/intval.mobile.js | 1 + app/www/static/js/intval.web.js | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/www/index.html b/app/www/index.html index 86f1631..2e3eab4 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -49,7 +49,7 @@
    - +
    diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index f0c768d..517a311 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -21,6 +21,7 @@ window.setDir = null; window.setExposure = null; window.setDelay = null; window.setCounter = null; +window.sequence = null; //ms var shutter = function (exposure) { diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index fbe2f92..cec5742 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -112,6 +112,7 @@ mobile.init = function () { window.setExposure = mobile.setExposure; window.setDelay = mobile.setDelay; window.setCounter = mobile.setCounter; + window.sequence = mobile.sequence; //show ble-specific fields in settings for (let i of bleInputs) { diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 6bff2b1..bb90cd0 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -165,5 +165,6 @@ web.init = function () { window.setDelay = web.setDelay; window.setExposure = web.setExposure; window.setCounter = web.setCounter; + window.sequence = web.sequence; console.log('started web') }; \ No newline at end of file From 360998e9a0764e44bb6bc41ac61de74f8b8b113a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:52:58 -0500 Subject: [PATCH 291/414] Using periods instead of commas. Starting to get tired. --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 8c3417e..0c05c12 100644 --- a/index.js +++ b/index.js @@ -459,7 +459,7 @@ function bSequence (obj, cb) { if (intval._state.sequence && sequence._state.active) { return sequence.setStop(() => { intval._state.sequence = false - log.info('sequence stop'. { method : 'ble', id : seq_id }) + log.info('sequence stop', { method : 'ble', id : seq_id }) return cb() }) } else { @@ -479,7 +479,7 @@ function bSequence (obj, cb) { console.timeEnd('sequence time') }) if (seq_id !== false) { - log.info('sequence start'. { method : 'ble', id : seq_id }) + log.info('sequence start', { method : 'ble', id : seq_id }) } return cb() } From 782bf04714d289299e037450968618cbc0760fda Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Wed, 20 Dec 2017 23:59:36 -0500 Subject: [PATCH 292/414] Start and stop worked in web lib, but GUI didn't. This is a fix that shuts off the "sequence" button when sequence is stopped. --- app/www/static/js/intval.web.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index bb90cd0..b4e677a 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -150,9 +150,15 @@ web.sequence = function () { } web.sequenceSuccess = function (res) { if (res.started && res.started != false) { + STATE.sequence = true; document.getElementById('seq').focus(); + if (!document.getElementById('seq').classList.contains('focus')) { + document.getElementById('seq').classList.add('focus'); + } } else if (res.stopped) { + STATE.sequence = false; document.getElementById('seq').blur(); + document.getElementById('seq').classList.remove('focus'); } } web.useJson = function (res) { From 9e13e0fb96030e3ab61d9ab7f79578c10e064ec5 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 21 Dec 2017 00:03:32 -0500 Subject: [PATCH 293/414] Log state to see why there are false positives --- app/www/static/js/intval.core.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 517a311..ab7fbf7 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -162,12 +162,6 @@ var setState = function (res) { exposure = shutter(STATE.exposure); exposureScale = scaleAuto(STATE.exposure); - if (res.sequence === true) { - STATE.sequence = true; - mobile.ble.active = true; - document.getElementById('seq').classList.add('focus'); - } - document.getElementById('str').value = exposure.str; document.getElementById('scale').value = exposureScale; setExposureScale(); @@ -176,6 +170,13 @@ var setState = function (res) { delayScale = scaleAuto(STATE.delay); document.getElementById('delayScale').value = delayScale; setDelayScale(); + + if (res.sequence == true) { + console.log(res.sequence); + STATE.sequence = true; + mobile.ble.active = true; + document.getElementById('seq').classList.add('focus'); + } }; var appPage = function () { unsetPages(); From fdc43fdc3758a0a129d105bb3510108b50ba43d4 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 21 Dec 2017 00:15:55 -0500 Subject: [PATCH 294/414] Get state after finishing sequence, to update UI --- app/www/static/js/intval.core.js | 1 - app/www/static/js/intval.web.js | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index ab7fbf7..b710177 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -172,7 +172,6 @@ var setState = function (res) { setDelayScale(); if (res.sequence == true) { - console.log(res.sequence); STATE.sequence = true; mobile.ble.active = true; document.getElementById('seq').classList.add('focus'); diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index b4e677a..21653b3 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -159,6 +159,8 @@ web.sequenceSuccess = function (res) { STATE.sequence = false; document.getElementById('seq').blur(); document.getElementById('seq').classList.remove('focus'); + //update frontend at end of sequence + mobile.getState(); } } web.useJson = function (res) { From 30e1c52ab0acf443a7a49390486463c9a2d65e6b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 21 Dec 2017 00:17:19 -0500 Subject: [PATCH 295/414] Remove extraneous DOM manipulation from the sequenceSuccess function --- app/www/static/js/intval.mobile.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index cec5742..b6c1883 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -270,12 +270,8 @@ mobile.sequenceSuccess = function () { setTimeout(() => { if (STATE.sequence) { mobile.ble.active = true; - if (!document.getElementById('seq').classList.contains('focus')) { - document.getElementById('seq').classList.add('focus'); - } } else { mobile.ble.active = false; - document.getElementById('seq').classList.remove('focus'); } }, 20); } From 9fe6bec132d5708606883305fcefb62868dbad2b Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 22 Dec 2017 15:26:55 -0500 Subject: [PATCH 296/414] Install rpio --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 3c9f0f0..8133c5b 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "node-persist": "^2.1.0", "onoff": "^1.1.5", "restify": "^5.2.0", + "rpio": "^0.9.20", "sqlite3": "^3.1.13", "squel": "^5.12.0", "uuid": "^3.1.0", From 532ec2e679982d99958b6fa59f6af9239d23f0e0 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 22 Dec 2017 15:27:21 -0500 Subject: [PATCH 297/414] Install rpio --- package-lock.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/package-lock.json b/package-lock.json index 9926003..31999da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1614,6 +1614,15 @@ "glob": "6.0.4" } }, + "rpio": { + "version": "0.9.20", + "resolved": "https://registry.npmjs.org/rpio/-/rpio-0.9.20.tgz", + "integrity": "sha1-p9XZk6UhABVdHrehKal4E64a7aU=", + "requires": { + "bindings": "1.2.1", + "nan": "2.6.2" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", From 58c01c41089b6fc024df0ef250809aae40d4db00 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 22 Dec 2017 15:28:09 -0500 Subject: [PATCH 298/414] Change message on sequence button if running --- app/www/static/js/intval.core.js | 18 +++++++++++++++++- app/www/static/js/intval.web.js | 7 ++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index b710177..d05ba9d 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -174,9 +174,25 @@ var setState = function (res) { if (res.sequence == true) { STATE.sequence = true; mobile.ble.active = true; - document.getElementById('seq').classList.add('focus'); + seqState(true); } }; + +var seqState = function (state) { + const elem = document.getElementById('seq') + if (state) { + if (!elem.classList.contains('focus')) { + elem.classList.add('focus'); + elem.innerText = 'STOP SEQUENCE'; + } + } else { + if (elem.classList.contains('focus')) { + elem.classList.remove('focus'); + elem.innerText = 'START SEQUENCE'; + } + } +}; + var appPage = function () { unsetPages(); document.getElementById('app').classList.add('selected'); diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 21653b3..371c523 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -152,14 +152,11 @@ web.sequenceSuccess = function (res) { if (res.started && res.started != false) { STATE.sequence = true; document.getElementById('seq').focus(); - if (!document.getElementById('seq').classList.contains('focus')) { - document.getElementById('seq').classList.add('focus'); - } + seqState(true); } else if (res.stopped) { STATE.sequence = false; document.getElementById('seq').blur(); - document.getElementById('seq').classList.remove('focus'); - //update frontend at end of sequence + seqState(false); mobile.getState(); } } From bb41526516b7c15afe0ae386fb3681ae9e1920bf Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 22 Dec 2017 15:28:47 -0500 Subject: [PATCH 299/414] Intval.reset() instead of intval._setState() --- lib/intval/index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 287fd3c..cc8a6b4 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -51,6 +51,10 @@ intval._release = { intval._microDelay = 10 // delay after stop signal before stopping motors intval._pin = {} +/** + * + */ + intval.init = function () { if (!fs.existsSync('./state')) fs.mkdirSync('./state') storage.init({ @@ -66,7 +70,7 @@ intval.init = function () { forgiveParseErrors: false // [NEW] }).then(intval._restoreState).catch((err) => { log.warn('init', err) - intval._setState() + intval.reset() intval._declarePins() }) @@ -75,7 +79,6 @@ intval.init = function () { } intval._restoreState = function (res) { - //console.dir(res) storage.getItem('_state', 'test').then(intval._setState).catch((err) => { intval._setState(undefined) log.error('_restoreState', err) @@ -308,7 +311,7 @@ intval._releaseClosedState = function (now) { } intval.reset = function () { - intval._setState(undefined) + intval._setState() intval._storeState() } From 72b127f1341567061f5a1b6469149e4fb1edbeb5 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 22 Dec 2017 16:34:06 -0500 Subject: [PATCH 300/414] Change innerHTML, not innerText --- app/www/static/js/intval.core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index d05ba9d..1aafceb 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -183,12 +183,12 @@ var seqState = function (state) { if (state) { if (!elem.classList.contains('focus')) { elem.classList.add('focus'); - elem.innerText = 'STOP SEQUENCE'; + elem.innerHTML = 'STOP SEQUENCE'; } } else { if (elem.classList.contains('focus')) { elem.classList.remove('focus'); - elem.innerText = 'START SEQUENCE'; + elem.innerHTML = 'START SEQUENCE'; } } }; From 7ac6f89af973427bd57ba8d9e95913620d477789 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 23 Dec 2017 22:22:36 -0500 Subject: [PATCH 301/414] Only set ble active flag if on ble --- app/www/static/js/intval.core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 1aafceb..a43fb4d 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -173,7 +173,7 @@ var setState = function (res) { if (res.sequence == true) { STATE.sequence = true; - mobile.ble.active = true; + if (mobile.ble) mobile.ble.active = true; seqState(true); } }; From f044c0eee3243b75d19d4156b9096d3a60f3a7b7 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 23 Dec 2017 22:23:09 -0500 Subject: [PATCH 302/414] Highlight camera directon when selected --- app/www/static/css/index.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index b7e093b..0646b1e 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -160,6 +160,17 @@ button.focus{ #fwdLabel,#bwdLabel{ margin-top: 8px; + color: #666; +} +#fwdLabel.selected, +#bwdLabel.selected{ + color: #fff; +} +#fwdLabel.selected{ + text-shadow: 0px 0px 4px #20ce45; +} +#bwdLabel.selected{ + text-shadow: 0px 0px 4px #f32121; } #fwdLabel{ float: left; From 727ec7cae08eec8396bac7ddab9534cf2201e360 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 23 Dec 2017 22:30:17 -0500 Subject: [PATCH 303/414] Set innerHTML of non-input elements, not value. This is why labels are not changing. --- app/www/index.html | 2 +- app/www/static/js/intval.core.js | 13 ++++++++----- app/www/static/js/intval.web.js | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 2e3eab4..4bc3fce 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -52,7 +52,7 @@
    - +
    -

    INTVAL

    +

    INTVAL3

    Counter
    From 9d3a4dd22d64a6bccddd665beedabd05b09b7aa2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 23:22:15 -0500 Subject: [PATCH 314/414] Update readme language --- Readme.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 2034e74..61a10a4 100644 --- a/Readme.md +++ b/Readme.md @@ -1,2 +1,7 @@ -# intval3 +# INTVAL 3 +What is this? + +The INTVAL3 is an open source intervalometer for the Bolex 16mm camera. The goal of the project is to create a cheap-to-make intervalometer that can be used to automate timelapse or animation capture using mobile, web or physical controls. + +This is the third incarnation of the INTVAL project, this time utilizing the Raspberry Pi Zero W for Wifi and Bluetooth control. Earlier versions, the INTVAL and INTVAL2 were Arduino-based. \ No newline at end of file From 635f6aee0bfd5987ae892c2f97b079e002b17c8f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 23:33:02 -0500 Subject: [PATCH 315/414] ignore problem files --- app/res/icon/ios/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 app/res/icon/ios/.gitignore diff --git a/app/res/icon/ios/.gitignore b/app/res/icon/ios/.gitignore new file mode 100644 index 0000000..9931186 --- /dev/null +++ b/app/res/icon/ios/.gitignore @@ -0,0 +1,3 @@ +/Icon-60.png +/Icon-60@2x.png +/Icon-60@3x.png From 17ad081607750bc10efbacd0c79f015a40c79869 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 23:59:27 -0500 Subject: [PATCH 316/414] Use an anyany screen for all ios devices --- app/config.xml | 8 +------- ...niversal.png => Default@2x~universal~anyany.png} | Bin 2 files changed, 1 insertion(+), 7 deletions(-) rename app/res/screen/ios/{INTVAL3_SCREEN_universal.png => Default@2x~universal~anyany.png} (100%) diff --git a/app/config.xml b/app/config.xml index 083cdde..e0a4bc0 100644 --- a/app/config.xml +++ b/app/config.xml @@ -22,10 +22,6 @@ - - - - @@ -33,9 +29,7 @@ - - - + diff --git a/app/res/screen/ios/INTVAL3_SCREEN_universal.png b/app/res/screen/ios/Default@2x~universal~anyany.png similarity index 100% rename from app/res/screen/ios/INTVAL3_SCREEN_universal.png rename to app/res/screen/ios/Default@2x~universal~anyany.png From 60cdbcdf257bbda12bacf1218005af3dc4a75d1b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 30 Dec 2017 00:00:55 -0500 Subject: [PATCH 317/414] Carriage belonged to the mcopy project, was misplaced here --- hardware/carriage.scad | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 hardware/carriage.scad diff --git a/hardware/carriage.scad b/hardware/carriage.scad deleted file mode 100644 index ebf2c66..0000000 --- a/hardware/carriage.scad +++ /dev/null @@ -1,10 +0,0 @@ -THREADED_R = 12 / 2; -ROD_R = 13 / 2; - -module threaded_rod () { - $fn = 60; - length = 30 * 10; - cylinder(r = THREADED_R, h = length, center = true); -} - -translate([45, 0, 0]) rotate([90, 0, 0]) threaded_rod(); \ No newline at end of file From d62a83e4ffe39ead315b031de6e6c96447dc5b2e Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 30 Dec 2017 00:49:31 -0500 Subject: [PATCH 318/414] Add beginnings of parts list and project description --- Readme.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 61a10a4..849c805 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,27 @@ -# INTVAL 3 +# INTVAL3 -What is this? +### What is this? -The INTVAL3 is an open source intervalometer for the Bolex 16mm camera. The goal of the project is to create a cheap-to-make intervalometer that can be used to automate timelapse or animation capture using mobile, web or physical controls. +INTVAL3 is an open source intervalometer for the Bolex 16mm camera. The goal of the project is to create a cheap-to-make intervalometer that can be used to automate time-lapse or animation on the Bolex using mobile, web or physical controls. -This is the third incarnation of the INTVAL project, this time utilizing the Raspberry Pi Zero W for Wifi and Bluetooth control. Earlier versions, the INTVAL and INTVAL2 were Arduino-based. \ No newline at end of file +This is the third incarnation of the INTVAL project, this time utilizing the [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/) for Wifi and Bluetooth control. Earlier versions, the [INTVAL](https://github.com/sixteenmillimeter/INTVAL) and [INTVAL2](https://github.com/sixteenmillimeter/intval2) were Arduino-based. The original INTVAL used a solenoid (!!!) to hammer a camera release cable, while the second attempt was a proving ground for the motor-and-key hardware used in this version. + +The [INTVAL2](https://github.com/sixteenmillimeter/intval2) project should be used if you prefer a simpler, physical interface approach. + +### Components + +* Firmware for the Raspberry Pi Zero W running on [Node.js](https://nodejs.org) +* Mobile/Web/API for controlling device, using [Cordova](https://cordova.apache.org/) + [Bleno](https://github.com/sandeepmistry/bleno), and [Restify](http://restify.com/) +* Hardware files, parts models for 3D printing, laser cutting and CNC +* PCB design for a Raspberry Pi HAT +* [Parts list](#parts-list) + +
    + +## PARTS + +1. Raspberry Pi Zero W - [[Amazon](http://amzn.to/2BWkKAy)] [[Adafruit](https://www.adafruit.com/product/3400)] +2. L298N Breakout Board +[[Amazon](http://amzn.to/2DwBrmz)] +3. 120RPM 12VDC Motor +[[Amazon](http://amzn.to/2CbRw4R)] \ No newline at end of file From a1ced19b42b90817c6bd42ebd71706ae95d901a5 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 01:26:29 -0500 Subject: [PATCH 319/414] Move all config files into ./conf --- {hardware => conf}/network_interfaces.conf | 0 nginx.conf => conf/nginx.conf | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {hardware => conf}/network_interfaces.conf (100%) rename nginx.conf => conf/nginx.conf (100%) diff --git a/hardware/network_interfaces.conf b/conf/network_interfaces.conf similarity index 100% rename from hardware/network_interfaces.conf rename to conf/network_interfaces.conf diff --git a/nginx.conf b/conf/nginx.conf similarity index 100% rename from nginx.conf rename to conf/nginx.conf From d3a7a97c78186111af33dc6351766762a8215b06 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 20:55:54 -0500 Subject: [PATCH 320/414] Wifi module test. Use wpa_passphrase to generate a safe password hash --- lib/wifi/index.js | 24 ++++++++++++++++++++++++ tests/wifi.js | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 tests/wifi.js diff --git a/lib/wifi/index.js b/lib/wifi/index.js index b07d74a..7040341 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -110,6 +110,30 @@ class Wifi { //this._callback(null, { ssid : ssid, pwd : pwd.length }) _cb = () => {} } + /** + * (internal function) Create sanitized wpa_supplicant.conf stanza for + * configuring wifi without storing plaintext passwords + * @example + * network={ + * ssid="YOUR_SSID" + * #psk="YOUR_PASSWORD" + * psk=6a24edf1592aec4465271b7dcd204601b6e78df3186ce1a62a31f40ae9630702 + * } + * + * @param {string} ssid SSID of wifi network + * @param {string} + */ + _createPSK (ssid, pwd, callback) { + const cmd = `wpa_passphrase "${ssid}" "${pwd}" | grep "psk="` + let output + exec(cmd, (err, stdout, stderr) => { + if (err) { + return callback(err) + } + output = stdout.replace('#psk=', '').split('psk=')[1] + callback(null, output.trim()) + }) + } /** * Function which initializes the processes for adding a wifi access point authentication * diff --git a/tests/wifi.js b/tests/wifi.js new file mode 100644 index 0000000..d92fb8e --- /dev/null +++ b/tests/wifi.js @@ -0,0 +1,4 @@ +'use strict' + +const log = require('../lib/log')('wifi-tests') +const wifi = require('../lib/wifi') From 488b614e16053936782be741b72d15dfcddefae2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 21:06:42 -0500 Subject: [PATCH 321/414] Parse out hash and passphrase, mask passphrase when creating a record --- lib/wifi/index.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 7040341..698fcf3 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -125,13 +125,17 @@ class Wifi { */ _createPSK (ssid, pwd, callback) { const cmd = `wpa_passphrase "${ssid}" "${pwd}" | grep "psk="` - let output + let lines + let hash + let plaintext exec(cmd, (err, stdout, stderr) => { if (err) { return callback(err) } - output = stdout.replace('#psk=', '').split('psk=')[1] - callback(null, output.trim()) + lines = stdout.replace('#psk=', '').split('psk=') + hash = lines[1] + plaintext = lines[0] + callback(null, hash.trim(), plaintext.trim()) }) } /** @@ -139,10 +143,13 @@ class Wifi { * * @param {string} ssid SSID of network to configure * @param {string} pwd Password of access point, plaintext + * @param {string} hash Password/SSID of access point, securely hashed * @param {function} callback Function invoked after process is complete, or fails */ - setNetwork (ssid, pwd, callback) { - _entry = `network={\n\tssid="${ssid}"\n\tpsk="${pwd}"\n}\n` + setNetwork (ssid, pwd, hash, callback) { + let masked = pwd.split('').map(char => { return '*' }).join('') + console.log(pwd + ' vs. ' + masked) + _entry = `network={\n\tssid="${ssid}"\n\t#psk=${masked}\n\tpsk=${hash}\n}\n` _cb = callback _ssid = ssid fs.readFile(filePath, 'utf8', this._readConfigCb) From 43f62e4885bba500b004bb6f2969df88df3e2eb4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 21:23:31 -0500 Subject: [PATCH 322/414] wifi read characteristic in bleno and get network SSID on startup --- lib/ble/index.js | 23 +++++++++++++++++++++-- lib/wifi/index.js | 9 +++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 4351dea..65f52c9 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -80,8 +80,19 @@ function onWifiWrite (data, offset, withoutResponse, callback) { function onWifiRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS - const data = new Buffer(JSON.stringify(currentWifi)) - callback(result, data.slice(offset, data.length)) + const wifiRes = {} + let data + wifi.list((err, list) => { + if (err) { + result = bleno.Characteristic.RESULT_UNLIKELY_ERROR + return callback(result) + } + wifiRes.available = list + wifiRes.current = currentWifi + log.info('Discovered available APs', { found : list.length }) + data = new Buffer(JSON.stringify(wifiRes)) + callback(result, data.slice(offset, data.length)) + }) } function getMac () { @@ -162,6 +173,14 @@ class BLE { bleno.on('disconnect', clientAddress => { log.info('disconnect', { clientAddress : clientAddress }) }) + + wifi.getNetwork((err, ssid) => { + if (err) { + return log.error('wifi.getNetwork', err) + } + currentWifi = ssid + log.info('wifi.getNetwork', {ssid : ssid}) + }) } _onWrite (data, offset, withoutResponse, callback) { let result = {} diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 698fcf3..a07a4c6 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -142,13 +142,12 @@ class Wifi { * Function which initializes the processes for adding a wifi access point authentication * * @param {string} ssid SSID of network to configure - * @param {string} pwd Password of access point, plaintext + * @param {string} pwd Password of access point, plaintext to be masked * @param {string} hash Password/SSID of access point, securely hashed * @param {function} callback Function invoked after process is complete, or fails */ setNetwork (ssid, pwd, hash, callback) { - let masked = pwd.split('').map(char => { return '*' }).join('') - console.log(pwd + ' vs. ' + masked) + let masked = pwd.split('').map(char => { return char !== '"' ? '*' : '"' }).join('') _entry = `network={\n\tssid="${ssid}"\n\t#psk=${masked}\n\tpsk=${hash}\n}\n` _cb = callback _ssid = ssid @@ -160,11 +159,13 @@ class Wifi { * @param {function} callback Function which is invoked after command is completed */ getNetwork (callback) { + let output exec(iwgetid, (err, stdout, stderr) => { if (err) { return callback(err) } - callback(null, stdout) + output = stdout.split('ESSID:')[1].replace(quoteRe, '').trim() + callback(null, output) }) } } From 5b3d1ff2b3ca528660df56949a7ca094da012c1b Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 21:32:09 -0500 Subject: [PATCH 323/414] Update write characteristic to use hash function, making createPSK public function --- lib/ble/index.js | 21 ++++++++++++++------- lib/wifi/index.js | 7 ++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 65f52c9..2be0f0c 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -65,16 +65,23 @@ function onWifiWrite (data, offset, withoutResponse, callback) { ssid = obj.ssid pwd = obj.pwd log.info(`connecting to AP`, { ssid : ssid }) - return wifi.setNetwork(ssid, pwd, (err, data) => { + return wifi.createPSK(ssid, pwd, (err, hash, plaintext) => { if (err) { - log.error('Error configuring wifi', err) - result = bleno.Characteristic.RESULT_UNLIKELY_ERROR + log.error('Error hashing wifi password', err) + result = bleno.Characteristic.RESULT_UNLIKELY_ERROR return callback(result) } - currentWifi = ssid - log.info(`Connected to AP`, { ssid : ssid }) - result = bleno.Characteristic.RESULT_SUCCESS - return callback(result) + return wifi.setNetwork(ssid, plaintext, hash, (err, data) => { + if (err) { + log.error('Error configuring wifi', err) + result = bleno.Characteristic.RESULT_UNLIKELY_ERROR + return callback(result) + } + currentWifi = ssid + log.info(`Connected to AP`, { ssid : ssid }) + result = bleno.Characteristic.RESULT_SUCCESS + return callback(result) + }) }) } diff --git a/lib/wifi/index.js b/lib/wifi/index.js index a07a4c6..dc70d1d 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -111,7 +111,7 @@ class Wifi { _cb = () => {} } /** - * (internal function) Create sanitized wpa_supplicant.conf stanza for + * Create sanitized wpa_supplicant.conf stanza for * configuring wifi without storing plaintext passwords * @example * network={ @@ -121,9 +121,10 @@ class Wifi { * } * * @param {string} ssid SSID of wifi network - * @param {string} + * @param {string} pwd Plaintext passphrase of wifi network + * @param {function} callback Function called after psk hash is generated */ - _createPSK (ssid, pwd, callback) { + createPSK (ssid, pwd, callback) { const cmd = `wpa_passphrase "${ssid}" "${pwd}" | grep "psk="` let lines let hash From 0064f75e3bdbb390cbd3035f331a5a6c5d407f50 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 21:41:18 -0500 Subject: [PATCH 324/414] List wifi APs and log when process is starting --- lib/ble/index.js | 1 + lib/wifi/index.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 2be0f0c..b902854 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -89,6 +89,7 @@ function onWifiRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS const wifiRes = {} let data + log.info('Getting wifi data for onWifiRead') wifi.list((err, list) => { if (err) { result = bleno.Characteristic.RESULT_UNLIKELY_ERROR diff --git a/lib/wifi/index.js b/lib/wifi/index.js index dc70d1d..db57125 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -64,7 +64,7 @@ class Wifi { data = data.replace(networkPattern, _entry) } _entry = null - fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) + //fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** * (internal function) Invoked after config file is written, From 89199a509594e6cca806a188fa97cf9f453746a2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 22:03:42 -0500 Subject: [PATCH 325/414] Remove blank responses in wifi access point listing --- lib/wifi/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index db57125..59ae41c 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -43,6 +43,9 @@ class Wifi { output.push(line) } } + output = output.filter(ap => { + if (ap !== '') return ap + }) return callback(null, output) }) } From 9896bf1ad5bf1bcdefc067ecae3c0af1fe7a608c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 22:27:02 -0500 Subject: [PATCH 326/414] Array output must be let, not const, to filter before sending to device. --- lib/wifi/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 59ae41c..96d282c 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -34,7 +34,7 @@ class Wifi { return callback(err) } const lines = stdout.split('\n') - const output = [] + let output = [] let line for (let l of lines) { line = l.replace('ESSID:', '').trim() From 2c3b4408fb0bd24199f1e8468afd2d6fb0061438 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 23:16:01 -0500 Subject: [PATCH 327/414] Log excessively and use info to build parseConfig function --- lib/ble/index.js | 3 ++- lib/wifi/index.js | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index b902854..41d9e12 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -71,6 +71,8 @@ function onWifiWrite (data, offset, withoutResponse, callback) { result = bleno.Characteristic.RESULT_UNLIKELY_ERROR return callback(result) } + console.log(hash) + console.log(plaintext) return wifi.setNetwork(ssid, plaintext, hash, (err, data) => { if (err) { log.error('Error configuring wifi', err) @@ -89,7 +91,6 @@ function onWifiRead (offset, callback) { const result = bleno.Characteristic.RESULT_SUCCESS const wifiRes = {} let data - log.info('Getting wifi data for onWifiRead') wifi.list((err, list) => { if (err) { result = bleno.Characteristic.RESULT_UNLIKELY_ERROR diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 96d282c..5ddb54a 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -61,11 +61,15 @@ class Wifi { console.error(err) return _cb(err) } + console.dir(data) + console.log(data.search(networkPattern)) + console.log(_entry) if (data.search(networkPattern) === -1) { data += `\n${_entry}` } else { data = data.replace(networkPattern, _entry) } + console.dir(data) _entry = null //fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } @@ -112,6 +116,9 @@ class Wifi { console.log('Wifi refreshed') //this._callback(null, { ssid : ssid, pwd : pwd.length }) _cb = () => {} + } + _parseConfig () { + } /** * Create sanitized wpa_supplicant.conf stanza for From 15ded20735430a558b0edbf418de38c81f045def Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 23:45:40 -0500 Subject: [PATCH 328/414] Parse wpa_supplicant file --- lib/ble/index.js | 2 -- lib/wifi/index.js | 31 ++++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 41d9e12..2be0f0c 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -71,8 +71,6 @@ function onWifiWrite (data, offset, withoutResponse, callback) { result = bleno.Characteristic.RESULT_UNLIKELY_ERROR return callback(result) } - console.log(hash) - console.log(plaintext) return wifi.setNetwork(ssid, plaintext, hash, (err, data) => { if (err) { log.error('Error configuring wifi', err) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 5ddb54a..8f1c4b3 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -57,13 +57,18 @@ class Wifi { * @param {string} data Contents of the config file */ _readConfigCb (err, data) { + let parsed if (err) { console.error(err) return _cb(err) } - console.dir(data) + + parsed = this._parseConfig(data) + console.dir(parsed) + console.log(data) console.log(data.search(networkPattern)) console.log(_entry) + if (data.search(networkPattern) === -1) { data += `\n${_entry}` } else { @@ -117,8 +122,28 @@ class Wifi { //this._callback(null, { ssid : ssid, pwd : pwd.length }) _cb = () => {} } - _parseConfig () { - + _parseConfig (str) { + const networks = [] + const lines = str.split('\n') + let network = {} + for (let line of lines) { + if (line.indexOf('network={') !== -1) { + network = {} + network.raw = line + } else if (line.indexOf('ssid=') !== -1) { + network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') + if (network.raw) { + network.raw += '\n' + line + } + } else if (line.indexOf('}') !== -1) { + network.raw += '\n' + line + networks.push(network) + network = {} + } else if (network.ssid) { + network.raw += '\n' + line + } + } + return networks } /** * Create sanitized wpa_supplicant.conf stanza for From 781f6046e550ebe87ae0a8ef3d962093fc8878f3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 31 Dec 2017 23:52:00 -0500 Subject: [PATCH 329/414] parseConfig moved out of class --- lib/wifi/index.js | 50 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 8f1c4b3..7b643d7 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -17,6 +17,31 @@ let _entry = null let _ssid = null let _cb = null +//helper functions +function parseConfig (str) { + const networks = [] + const lines = str.split('\n') + let network = {} + for (let line of lines) { + if (line.indexOf('network={') !== -1) { + network = {} + network.raw = line + } else if (line.indexOf('ssid=') !== -1) { + network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') + if (network.raw) { + network.raw += '\n' + line + } + } else if (line.indexOf('}') !== -1) { + network.raw += '\n' + line + networks.push(network) + network = {} + } else if (network.ssid) { + network.raw += '\n' + line + } + } + return networks +} + /** Class representing the wifi features */ class Wifi { constructor () { @@ -63,7 +88,7 @@ class Wifi { return _cb(err) } - parsed = this._parseConfig(data) + parsed = parseConfig(data) console.dir(parsed) console.log(data) console.log(data.search(networkPattern)) @@ -122,29 +147,6 @@ class Wifi { //this._callback(null, { ssid : ssid, pwd : pwd.length }) _cb = () => {} } - _parseConfig (str) { - const networks = [] - const lines = str.split('\n') - let network = {} - for (let line of lines) { - if (line.indexOf('network={') !== -1) { - network = {} - network.raw = line - } else if (line.indexOf('ssid=') !== -1) { - network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') - if (network.raw) { - network.raw += '\n' + line - } - } else if (line.indexOf('}') !== -1) { - network.raw += '\n' + line - networks.push(network) - network = {} - } else if (network.ssid) { - network.raw += '\n' + line - } - } - return networks - } /** * Create sanitized wpa_supplicant.conf stanza for * configuring wifi without storing plaintext passwords From 7e58ac40aa7a3f904f9c5d0c71520f6324326e77 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:04:49 -0500 Subject: [PATCH 330/414] If entry is for an SSID that exists, replace old with new. If entry is for SSID that doesn't exist, add to file. --- lib/wifi/index.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 7b643d7..5b05255 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -83,24 +83,26 @@ class Wifi { */ _readConfigCb (err, data) { let parsed + let current if (err) { console.error(err) return _cb(err) } - + console.log(data) parsed = parseConfig(data) console.dir(parsed) - console.log(data) - console.log(data.search(networkPattern)) - console.log(_entry) - - if (data.search(networkPattern) === -1) { - data += `\n${_entry}` + current = parsed.filter(network => { + return (network.ssid + '').toLowerCase() === (_ssid + '').toLowerCase() + }) + console.log(current) + if (typeof current !== 'undefined') { + data = data.replace(current.raw, _entry) } else { - data = data.replace(networkPattern, _entry) + data += '\n\n' + _entry } - console.dir(data) + console.log(data) _entry = null + this._writeConfigCb(null) //fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** From bcc29424da50b78c922e1c11b501a55c946715a0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:10:50 -0500 Subject: [PATCH 331/414] Caught mixed usage, use .find() not .filter() to locate a single entry. Test exising methods now. --- lib/wifi/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 5b05255..0b937b8 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -91,8 +91,8 @@ class Wifi { console.log(data) parsed = parseConfig(data) console.dir(parsed) - current = parsed.filter(network => { - return (network.ssid + '').toLowerCase() === (_ssid + '').toLowerCase() + current = parsed.find(network => { + return network.ssid === _ssid }) console.log(current) if (typeof current !== 'undefined') { @@ -102,7 +102,7 @@ class Wifi { } console.log(data) _entry = null - this._writeConfigCb(null) + setTimeout(this._writeConfigCb, 1) //fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** From 54968816167d1bfc5bd8678e1fb18282ecf25989 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:19:07 -0500 Subject: [PATCH 332/414] Improve parser to ignore stanzas that are commented out or not formatted by this program. --- lib/wifi/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 0b937b8..cd7b8dd 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -23,19 +23,19 @@ function parseConfig (str) { const lines = str.split('\n') let network = {} for (let line of lines) { - if (line.indexOf('network={') !== -1) { + if (line.substring(0, 9) === 'network={') { network = {} network.raw = line - } else if (line.indexOf('ssid=') !== -1) { + } else if (network.raw && line.indexOf('ssid=') !== -1) { network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') if (network.raw) { network.raw += '\n' + line } - } else if (line.indexOf('}') !== -1) { + } else if (network.raw && line.substring(0, 1) === '}') { network.raw += '\n' + line networks.push(network) network = {} - } else if (network.ssid) { + } else if (network.raw) { network.raw += '\n' + line } } From 4ce4ac273516df9e23a78242bb329dd8fa8fbdec Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:28:11 -0500 Subject: [PATCH 333/414] Restore wifi functionality for writing full config file. --- lib/wifi/index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index cd7b8dd..34c76b2 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -88,22 +88,17 @@ class Wifi { console.error(err) return _cb(err) } - console.log(data) parsed = parseConfig(data) - console.dir(parsed) current = parsed.find(network => { return network.ssid === _ssid }) - console.log(current) if (typeof current !== 'undefined') { data = data.replace(current.raw, _entry) } else { data += '\n\n' + _entry } - console.log(data) _entry = null - setTimeout(this._writeConfigCb, 1) - //fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) + fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) } /** * (internal function) Invoked after config file is written, From 83ed21441cac5e440a64052b31fc53854e817679 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:34:24 -0500 Subject: [PATCH 334/414] Bind functions to this --- lib/wifi/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index 34c76b2..de66af3 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -98,7 +98,7 @@ class Wifi { data += '\n\n' + _entry } _entry = null - fs.writeFile(filePath, data, 'utf8', this._writeConfigCb) + fs.writeFile(filePath, data, 'utf8', this._writeConfigCb.bind(this)) } /** * (internal function) Invoked after config file is written, @@ -111,7 +111,7 @@ class Wifi { console.error(err) return _cb(err) } - exec(reconfigure, this._reconfigureCb) + exec(reconfigure, this._reconfigureCb.bind(this)) } /** * (internal function) Invoked after reconfiguration command is complete @@ -126,7 +126,7 @@ class Wifi { return _cb(err) } console.log('Wifi reconfigured') - exec(refresh, this._refreshCb) + exec(refresh, this._refreshCb.bind(this)) } /** * (internal function) Invoked after wifi refresh command is complete @@ -141,7 +141,7 @@ class Wifi { return _cb(err) } console.log('Wifi refreshed') - //this._callback(null, { ssid : ssid, pwd : pwd.length }) + _cb(null, { ssid : ssid, pwd : pwd.length }) _cb = () => {} } /** @@ -186,7 +186,7 @@ class Wifi { _entry = `network={\n\tssid="${ssid}"\n\t#psk=${masked}\n\tpsk=${hash}\n}\n` _cb = callback _ssid = ssid - fs.readFile(filePath, 'utf8', this._readConfigCb) + fs.readFile(filePath, 'utf8', this._readConfigCb.bind(this)) } /** * Executes command which gets the currently connected network From 40a306c9e185cd6b7f9dc42a1cce70052cfe35c1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:37:00 -0500 Subject: [PATCH 335/414] Callback reads _ssid not ssid --- lib/wifi/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index de66af3..fc9a58a 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -141,7 +141,7 @@ class Wifi { return _cb(err) } console.log('Wifi refreshed') - _cb(null, { ssid : ssid, pwd : pwd.length }) + _cb(null, { ssid : _ssid }) _cb = () => {} } /** From 0b5e4c47d3998a0194b82ff62b0849aa454da2e6 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:38:08 -0500 Subject: [PATCH 336/414] parseConfig rejoins the class as _parseConfig --- lib/wifi/index.js | 50 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index fc9a58a..c28a6d8 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -17,31 +17,6 @@ let _entry = null let _ssid = null let _cb = null -//helper functions -function parseConfig (str) { - const networks = [] - const lines = str.split('\n') - let network = {} - for (let line of lines) { - if (line.substring(0, 9) === 'network={') { - network = {} - network.raw = line - } else if (network.raw && line.indexOf('ssid=') !== -1) { - network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') - if (network.raw) { - network.raw += '\n' + line - } - } else if (network.raw && line.substring(0, 1) === '}') { - network.raw += '\n' + line - networks.push(network) - network = {} - } else if (network.raw) { - network.raw += '\n' + line - } - } - return networks -} - /** Class representing the wifi features */ class Wifi { constructor () { @@ -88,7 +63,7 @@ class Wifi { console.error(err) return _cb(err) } - parsed = parseConfig(data) + parsed = this._parseConfig(data) current = parsed.find(network => { return network.ssid === _ssid }) @@ -144,6 +119,29 @@ class Wifi { _cb(null, { ssid : _ssid }) _cb = () => {} } + _parseConfig (str) { + const networks = [] + const lines = str.split('\n') + let network = {} + for (let line of lines) { + if (line.substring(0, 9) === 'network={') { + network = {} + network.raw = line + } else if (network.raw && line.indexOf('ssid=') !== -1) { + network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '') + if (network.raw) { + network.raw += '\n' + line + } + } else if (network.raw && line.substring(0, 1) === '}') { + network.raw += '\n' + line + networks.push(network) + network = {} + } else if (network.raw) { + network.raw += '\n' + line + } + } + return networks + } /** * Create sanitized wpa_supplicant.conf stanza for * configuring wifi without storing plaintext passwords From 68dab59e6ba2bd259edce19d189ebb25c4b78869 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 00:52:29 -0500 Subject: [PATCH 337/414] Bluetooth sequence logic was waiting for a callback that never came... --- index.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 0c05c12..7221ced 100644 --- a/index.js +++ b/index.js @@ -457,11 +457,10 @@ function bSequence (obj, cb) { } } if (intval._state.sequence && sequence._state.active) { - return sequence.setStop(() => { - intval._state.sequence = false - log.info('sequence stop', { method : 'ble', id : seq_id }) - return cb() - }) + sequence.setStop() + intval._state.sequence = false + log.info('sequence stop', { method : 'ble', id : seq_id }) + return cb() } else { console.time('sequence time') intval._state.sequence = true From 93387b27f2db51d00f9cd27d48b7a2296ab30a34 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 1 Jan 2018 01:08:59 -0500 Subject: [PATCH 338/414] Seq_id doesn't exist in a stop context --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7221ced..61ad0d9 100644 --- a/index.js +++ b/index.js @@ -459,7 +459,7 @@ function bSequence (obj, cb) { if (intval._state.sequence && sequence._state.active) { sequence.setStop() intval._state.sequence = false - log.info('sequence stop', { method : 'ble', id : seq_id }) + log.info('sequence stop', { method : 'ble' }) return cb() } else { console.time('sequence time') From 6b59b15d62fc37579df60c753d4a2ae13199d8b8 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 2 Jan 2018 00:34:03 -0500 Subject: [PATCH 339/414] Improve parts list with links --- Readme.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 849c805..d777fe6 100644 --- a/Readme.md +++ b/Readme.md @@ -13,15 +13,16 @@ The [INTVAL2](https://github.com/sixteenmillimeter/intval2) project should be us * Firmware for the Raspberry Pi Zero W running on [Node.js](https://nodejs.org) * Mobile/Web/API for controlling device, using [Cordova](https://cordova.apache.org/) + [Bleno](https://github.com/sandeepmistry/bleno), and [Restify](http://restify.com/) * Hardware files, parts models for 3D printing, laser cutting and CNC -* PCB design for a Raspberry Pi HAT +* PCB design for a Raspberry Pi Zero W Bonnet * [Parts list](#parts-list) ## PARTS -1. Raspberry Pi Zero W - [[Amazon](http://amzn.to/2BWkKAy)] [[Adafruit](https://www.adafruit.com/product/3400)] -2. L298N Breakout Board -[[Amazon](http://amzn.to/2DwBrmz)] -3. 120RPM 12VDC Motor -[[Amazon](http://amzn.to/2CbRw4R)] \ No newline at end of file +1. Raspberry Pi Zero W - [[Amazon](http://amzn.to/2BWkKAy)] [[Adafruit](https://www.adafruit.com/product/3400)] [[Sparkfun](https://www.sparkfun.com/products/14277)] +2. L298N Breakout Board - [[Amazon](http://amzn.to/2DwBrmz)] +3. 120RPM 12VDC Motor - [[Amazon](http://amzn.to/2CbRw4R)] +4. Microswitch w/ Roller - [[Amazon](http://amzn.to/2CdG4FW)] [[Adafruit](https://www.adafruit.com/product/819)] +5. L7805 5V Regulator - [[Amazon](http://amzn.to/2CeFNlZ)] [[Adafruit](https://www.adafruit.com/product/2164)] [Sparkfun](https://www.sparkfun.com/products/107)] +6. (Optional) Proto Bonnet - [[Amazon](http://amzn.to/2ChnSLm)] [Adafruit](https://www.adafruit.com/product/3203) \ No newline at end of file From 7d5f6c6601e00db0b31d05ead30b39bc4fe3cecd Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 2 Jan 2018 00:35:20 -0500 Subject: [PATCH 340/414] Mobile app now allows user to interact with wifi settings. Also (mostly) properly displays the state of a sequence. --- app/www/index.html | 9 ++- app/www/static/css/index.css | 10 +++ app/www/static/js/intval.core.js | 2 + app/www/static/js/intval.mobile.js | 117 ++++++++++++++++++++++++++++- 4 files changed, 133 insertions(+), 5 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 0e6fe49..537b0fd 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -76,12 +76,15 @@

    WIFI

    - +
    - +
    - + +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 0646b1e..9bdb448 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -294,3 +294,13 @@ footer > div.selected{ #scan.active{ display: block; } + +#available.active{ + border-color: #20ce45;; +} +#password,#wifi{ + display: none; +} +#password.active,#wifi.active{ + display: block; +} \ No newline at end of file diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index a59a816..b542bf2 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -181,6 +181,8 @@ var setState = function (res) { STATE.sequence = true; if (mobile.ble) mobile.ble.active = true; seqState(true); + } else { + seqState(false); } }; diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index e3dc43f..d38a2dc 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -13,6 +13,11 @@ mobile.ble = { active : false }; +mobile.wifi = { + current : 'null', + available : [] +}; + mobile.ble.scan = function () { spinnerShow(); ble.scan([], 5, mobile.ble.onDiscover, mobile.ble.onError); @@ -69,6 +74,7 @@ mobile.ble.onConnect = function (peripheral, device) { scan.classList.remove('active'); getState(); + mobile.getWifi(); }; mobile.ble.disconnect = function () { @@ -266,18 +272,125 @@ mobile.sequence = function () { mobile.ble.active = true; }; - mobile.sequenceSuccess = function () { console.log('Sequence state changed'); mobile.getState(); setTimeout(() => { if (STATE.sequence) { mobile.ble.active = true; + seqState(true); } else { mobile.ble.active = false; + seqState(false); } }, 20); -} +}; + +//retreive object with list of available Wifi APs, +//and state of current connection, if available +mobile.getWifi = function () { + spinnerShow(); + ble.read(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.WIFI_ID, + mobile.getWifiSuccess, + mobile.ble.onError); +}; + +mobile.getWifiSuccess = function (data) { + const elem = document.getElementById('available'); + const wifi = document.getElementById('wifi'); + const password = document.getElementById('password'); + let option = document.createElement('option'); + let str = bytesToString(data); + let res = JSON.parse(str); + + spinnerHide(); + elem.innerHTML = '' + if (!res.available || res.available.length === 0) { + if (elem.classList.contains('active')) { + elem.classList.remove('active'); + } + option.text = 'N/A' + elem.add(option); + elem.value = ''; + } else { + for (let ap of res.available) { + option = document.createElement('option'); + option.text = ap; + option.value = ap; + elem.add(option); + } + if (res.current && res.available.indexOf(res.current) !== -1) { + elem.value = res.current + if (!elem.classList.contains('active')) { + elem.classList.add('active'); + } + if (wifi.classList.contains('active')) { + wifi.classList.remove('active'); + } + if (password.classList.contains('active')) { + password.classList.remove('active'); + } + } else { + if (!wifi.classList.contains('active')) { + wifi.classList.add('active'); + } + if (!password.classList.contains('active')) { + password.classList.add('active'); + } + } + } + mobile.wifi.current = res.current; + mobile.wifi.available = res.available; +}; + +mobile.editWifi = function () { + const available = document.getElementById('available'); + const wifi = document.getElementById('wifi'); + const password = document.getElementById('password'); + if (!wifi.classList.contains('active')) { + wifi.classList.add('active'); + } + if (!password.classList.contains('active')) { + password.classList.add('active'); + } + password.focus(); + if (available.value !== mobile.wifi.current && available.classList.contains('active')) { + available.classList.remove('active'); + } +}; + +mobile.setWifi = function () { + const ssid = document.getElementById('available').value; + const pwd = document.getElementById('password').value; + const opts = { + ssid : ssid, + pwd : pwd + } + spinnerShow(); + if (ssid === '' || ssid === null || ssid === undefined) { + return alert('Cannot set wireless credentials with a blank SSID'); + } + if (pwd === '' || pwd === null || pwd === undefined) { + return alert('Cannot set wireless credentials with a blank passphrase'); + } + if (pwd.length < 8 || pwd.length > 63) { + return alert('Passphrase must be 8..63 characters'); + } + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.WIFI_ID, + stringToBytes(JSON.stringify(opts)), + mobile.setWifiSuccess, + mobile.ble.onError); +}; + +mobile.setWifiSuccess = function () { + spinnerHide(); + console.log('Set new wifi credentials'); + setTimeout(mobile.getWifi, 100); +}; function bytesToString (buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); From 3af05e442d4f6e64289a6160c01def75d9e3c0ef Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 4 Jan 2018 16:10:38 -0500 Subject: [PATCH 341/414] Track the local ip address of the wlan0 interface and report information to the BLE service. --- lib/ble/index.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/ble/index.js b/lib/ble/index.js index 2be0f0c..8bb39ea 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -22,6 +22,7 @@ const bleno = require('bleno') let currentWifi = 'disconnected' +let currentAddr = null let getState const chars = [] @@ -78,6 +79,7 @@ function onWifiWrite (data, offset, withoutResponse, callback) { return callback(result) } currentWifi = ssid + currentAddr = getIp() log.info(`Connected to AP`, { ssid : ssid }) result = bleno.Characteristic.RESULT_SUCCESS return callback(result) @@ -96,6 +98,7 @@ function onWifiRead (offset, callback) { } wifiRes.available = list wifiRes.current = currentWifi + wifiRes.ip = currentAddr log.info('Discovered available APs', { found : list.length }) data = new Buffer(JSON.stringify(wifiRes)) callback(result, data.slice(offset, data.length)) @@ -133,6 +136,23 @@ function spoofMac () { return MACTMP } +function getIp () { + let addr = null + let ipv4 + const ifaces = os.networkInterfaces() + if (ifaces && ifaces.wlan0) { + ipv4 = ifaces.wlan0.filter(iface => { + if (iface.family === 'IPv4') { + return iface + } + }) + if (ipv4.length === 1) { + addr = ipv4.address + } + } + return addr +} + function capitalize (s) { return s[0].toUpperCase() + s.slice(1) @@ -186,6 +206,7 @@ class BLE { return log.error('wifi.getNetwork', err) } currentWifi = ssid + currentAddr = getIp() log.info('wifi.getNetwork', {ssid : ssid}) }) } From 980f1f56c5566e7f97b693edc7af41d8113f82ea Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 4 Jan 2018 16:12:32 -0500 Subject: [PATCH 342/414] Log the local ip so info is readable server-side as well. --- lib/ble/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index 8bb39ea..cce3991 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -80,7 +80,7 @@ function onWifiWrite (data, offset, withoutResponse, callback) { } currentWifi = ssid currentAddr = getIp() - log.info(`Connected to AP`, { ssid : ssid }) + log.info(`Connected to AP`, { ssid : ssid, ip : currentAddr }) result = bleno.Characteristic.RESULT_SUCCESS return callback(result) }) @@ -207,7 +207,7 @@ class BLE { } currentWifi = ssid currentAddr = getIp() - log.info('wifi.getNetwork', {ssid : ssid}) + log.info('wifi.getNetwork', {ssid : ssid, ip : currentAddr }) }) } _onWrite (data, offset, withoutResponse, callback) { From df1c39604a7a7ca25e2057db95be267268d8df22 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 4 Jan 2018 16:14:12 -0500 Subject: [PATCH 343/414] Referenced the filtered array, not the first element, like a jackass. --- lib/ble/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ble/index.js b/lib/ble/index.js index cce3991..2332fd8 100644 --- a/lib/ble/index.js +++ b/lib/ble/index.js @@ -147,7 +147,7 @@ function getIp () { } }) if (ipv4.length === 1) { - addr = ipv4.address + addr = ipv4[0].address } } return addr From 34eefbd0671a24d57f375bc49a6cb6348accd62c Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Thu, 4 Jan 2018 16:25:22 -0500 Subject: [PATCH 344/414] Hide and show IP address based on availability --- app/www/index.html | 3 +++ app/www/static/css/index.css | 12 +++++++++--- app/www/static/js/intval.mobile.js | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 537b0fd..a37d3b6 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -80,6 +80,9 @@
    +
    + Local IP: null +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 9bdb448..931e032 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -296,11 +296,17 @@ footer > div.selected{ } #available.active{ - border-color: #20ce45;; + border-color: #20ce45; } -#password,#wifi{ +#ip{ + color: #666; +} +#ip span{ + color: #20ce45; +} +#password,#wifi,#ip{ display: none; } -#password.active,#wifi.active{ +#password.active,#ip.active,#wifi.active{ display: block; } \ No newline at end of file diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index d38a2dc..4a98ff9 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -15,7 +15,8 @@ mobile.ble = { mobile.wifi = { current : 'null', - available : [] + available : [], + ip : null }; mobile.ble.scan = function () { @@ -301,6 +302,7 @@ mobile.getWifiSuccess = function (data) { const elem = document.getElementById('available'); const wifi = document.getElementById('wifi'); const password = document.getElementById('password'); + const ip = document.getElementById('ip'); let option = document.createElement('option'); let str = bytesToString(data); let res = JSON.parse(str); @@ -341,8 +343,20 @@ mobile.getWifiSuccess = function (data) { } } } + if (typeof res.ip !== 'undefined' && res.ip != null ) { + ip.innerHTML = `Local IP: ${res.ip}` + if (!ip.classList.contains('active')) { + ip.classList.add('active'); + } + } else { + ip.innerHTML = 'Local IP: null' + if (ip.classList.contains('active')) { + ip.classList.remove('active'); + } + } mobile.wifi.current = res.current; mobile.wifi.available = res.available; + mobile.wifi.ip = res.ip; }; mobile.editWifi = function () { From 6c28e729e113d94db93f6f3bf9c8b69290b2b94f Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 4 Jan 2018 16:32:59 -0500 Subject: [PATCH 345/414] Only add padding to top of app if in mobile version --- app/www/static/css/index.css | 2 +- app/www/static/js/intval.mobile.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 931e032..f765575 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -36,7 +36,7 @@ html,body,button,h2,label,input{ font-family: 'Arial Neue', Helvetical, Arial, sans-serif; } -body{ +body.mobile{ padding-top: 35px; } .page{ diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 4a98ff9..7315826 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -112,6 +112,7 @@ mobile.ble.onError = function (err) { mobile.init = function () { const bleInputs = document.querySelectorAll('.ble') + document.querySelector('body').classList.add('mobile') window.frame = mobile.frame; window.getState = mobile.getState; From 0d251b212e70864b6a2e3b1a3c5ac21f60c89992 Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 5 Jan 2018 17:31:00 -0500 Subject: [PATCH 346/414] Add path module to db to better format url? Why is database no longer writing? Move ip above list of wifi. List package.version in app server creation. --- app/www/index.html | 6 +++--- index.js | 2 +- lib/db/index.js | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index a37d3b6..be93128 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -75,14 +75,14 @@

    WIFI

    +
    + Local IP: null +
    -
    - Local IP: null -
    diff --git a/index.js b/index.js index 61ad0d9..2937f23 100644 --- a/index.js +++ b/index.js @@ -15,7 +15,7 @@ const INDEXPATH = './app/www/index.html' let app = restify.createServer({ name: APPNAME, - version: '0.0.1' + version: PACKAGE.version }) let ble diff --git a/lib/db/index.js b/lib/db/index.js index b391538..22865d3 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -1,9 +1,11 @@ 'use strict' const os = require('os') +const path = require('path') const sqlite3 = require('sqlite3').verbose() const squel = require('squel') -const DB_FILE = os.homedir() + '/.intval3.db' + +const DB_FILE = path.join(os.homedir(), '.intval3.db') const db = new sqlite3.Database(DB_FILE) class DB { From 74af6218e033b613be390db645ae69ad2bf8c25a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 6 Jan 2018 10:54:00 -0500 Subject: [PATCH 347/414] Put db in config file (WHY IS THIS NOT WORKING) --- lib/db/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/db/index.js b/lib/db/index.js index 22865d3..4a790d9 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -1,11 +1,16 @@ 'use strict' +const fs = require('fs') const os = require('os') const path = require('path') const sqlite3 = require('sqlite3').verbose() const squel = require('squel') -const DB_FILE = path.join(os.homedir(), '.intval3.db') +const DB_DIR = path.join(os.homedir(), '.config/intval3/) +if (!fs.existsSync(DB_DIR)) { + fs.mkdirSync(DB_DIR) +} +const DB_FILE = path.join(os.homedir(), '.config/intval3/intval3.db') const db = new sqlite3.Database(DB_FILE) class DB { From 30e8636ea88e4904b5489900baaf2b5e1e470f9a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Sat, 6 Jan 2018 10:55:52 -0500 Subject: [PATCH 348/414] FUCK! The problem is that the script must run as root. Was looking in the wrong dir. --- lib/db/index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/db/index.js b/lib/db/index.js index 4a790d9..182ab6e 100644 --- a/lib/db/index.js +++ b/lib/db/index.js @@ -6,11 +6,7 @@ const path = require('path') const sqlite3 = require('sqlite3').verbose() const squel = require('squel') -const DB_DIR = path.join(os.homedir(), '.config/intval3/) -if (!fs.existsSync(DB_DIR)) { - fs.mkdirSync(DB_DIR) -} -const DB_FILE = path.join(os.homedir(), '.config/intval3/intval3.db') +const DB_FILE = path.join(os.homedir(), '.intval3.db') const db = new sqlite3.Database(DB_FILE) class DB { From 01f17830f8864a9dc2d057b2abf828553b78aaad Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 7 Jan 2018 18:05:24 -0500 Subject: [PATCH 349/414] Restore logging for microswitch state --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index cc8a6b4..98d7c6a 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -236,7 +236,7 @@ intval._watchMicro = function (err, val) { if (err) { log.error('_watchMicro', err) } - //log.info(`Microswitch val: ${val}`) + log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { if (!intval._state.micro.primed) { From b01a9e4483da99e9f337e40a80a3b375092eac6c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 8 Jan 2018 19:29:14 -0500 Subject: [PATCH 350/414] Potential fix, which prevents priming from happening after a determined interval --- lib/intval/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 98d7c6a..a2719ef 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -239,10 +239,11 @@ intval._watchMicro = function (err, val) { log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { + //&& (now - intval._state.frame.start) > 100 if (!intval._state.micro.primed) { intval._state.micro.primed = true intval._state.micro.time = now - //log.info('Microswitch primed to stop motor') + log.info('Microswitch primed to stop motor') } } else if (val === 1 && intval._state.frame.active) { if (intval._state.micro.primed && !intval._state.micro.paused && (now - intval._state.frame.start) > intval._frame.open) { From fb54403891326b1acecc8b012011739521fa3bce Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 8 Jan 2018 20:06:02 -0500 Subject: [PATCH 351/414] Pause before setting direction of frame. --- lib/intval/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/intval/index.js b/lib/intval/index.js index a2719ef..606833a 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -375,6 +375,7 @@ intval.frame = function (dir = null, exposure = null, cb = () => {}) { log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : exposure}) + intval._pause() if (dir) { intval._startFwd() } else { From b114ef8b0eb9a9bc6f2386c534aecc54f5cac99c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 8 Jan 2018 20:14:08 -0500 Subject: [PATCH 352/414] Hide spinner after 5 seconds, show message if not connected. --- app/www/static/js/intval.mobile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 7315826..c8d605c 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -24,8 +24,8 @@ mobile.ble.scan = function () { ble.scan([], 5, mobile.ble.onDiscover, mobile.ble.onError); mobile.ble.devices = []; setTimeout(() => { + spinnerHide(); if (!mobile.ble.connected) { - spinnerHide(); alert('No INTVAL devices found.'); settingsPage(); } @@ -128,6 +128,7 @@ mobile.init = function () { } spinnerInit(); mobile.ble.scan(); + }; mobile.getState = function () { From a9414c62125013032850ff762791fd820c8e4ea6 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 8 Jan 2018 22:08:41 -0500 Subject: [PATCH 353/414] Prevent motor from stopping before 100ms. Took out the logging in watchMicro, due to the fact that the problem with backwards frames was likely a bad connection. --- lib/intval/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 606833a..5bde0de 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -236,11 +236,10 @@ intval._watchMicro = function (err, val) { if (err) { log.error('_watchMicro', err) } - log.info(`Microswitch val: ${val}`) + //log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { - //&& (now - intval._state.frame.start) > 100 - if (!intval._state.micro.primed) { + if (!intval._state.micro.primed && (now - intval._state.frame.start) > 100) { intval._state.micro.primed = true intval._state.micro.time = now log.info('Microswitch primed to stop motor') @@ -375,7 +374,6 @@ intval.frame = function (dir = null, exposure = null, cb = () => {}) { log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : exposure}) - intval._pause() if (dir) { intval._startFwd() } else { From e55154e46e5156f89933fffb9d8eddd3ab9375f4 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 9 Jan 2018 00:03:20 -0500 Subject: [PATCH 354/414] Install "camera with exif" for capturing exif data with the mobile camera. This will eventually allow the user to take a photograph with their mobile camera and the Exposure time, ISO and f-stop can be used to establish an exposure value, that can be used to automatically set an exposure time for the Bolex to match the mobile camera's exposure setting. --- app/package-lock.json | 7 ++++++- app/package.json | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 53503a4..91ce2c0 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "com.sixteenmillimeter.intval3", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -531,6 +531,11 @@ "resolved": "https://registry.npmjs.org/cordova-plugin-ble-central/-/cordova-plugin-ble-central-1.1.4.tgz", "integrity": "sha1-rZA2mnla1wChuf3WbhnnzkSnicM=" }, + "cordova-plugin-camera-with-exif": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cordova-plugin-camera-with-exif/-/cordova-plugin-camera-with-exif-1.2.2.tgz", + "integrity": "sha1-/kxfHWgga6QoOqtNM4MrVSwil3A=" + }, "cordova-plugin-compat": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cordova-plugin-compat/-/cordova-plugin-compat-1.2.0.tgz", diff --git a/app/package.json b/app/package.json index 7120069..18eaf29 100644 --- a/app/package.json +++ b/app/package.json @@ -13,6 +13,7 @@ "cordova-android": "^6.4.0", "cordova-ios": "^4.5.4", "cordova-plugin-ble-central": "^1.1.4", + "cordova-plugin-camera-with-exif": "^1.2.2", "cordova-plugin-compat": "^1.2.0", "cordova-plugin-device": "^1.1.7", "cordova-plugin-dialogs": "^1.3.4", @@ -29,7 +30,8 @@ "cordova-plugin-ble-central": { "BLUETOOTH_USAGE_DESCRIPTION": "INTVAL intervalometer controls" }, - "cordova-plugin-splashscreen": {} + "cordova-plugin-splashscreen": {}, + "cordova-plugin-camera-with-exif": {} }, "platforms": [ "android", From 62769db3bf9898d764d7fca13dfbff1f63ee8bde Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 9 Jan 2018 00:06:17 -0500 Subject: [PATCH 355/414] Add a state for the mobile app to trigger the camera and set Bolex camera values. --- app/www/index.html | 15 +++++++ app/www/static/css/index.css | 32 ++++++++++++++ app/www/static/js/intval.core.js | 5 +++ app/www/static/js/intval.mobile.js | 71 ++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) diff --git a/app/www/index.html b/app/www/index.html index be93128..a9aeac4 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -57,6 +57,10 @@

    SETTINGS

    +
    ISO
    + +
    F-stop
    +
    Rex-o-fader
    +
    F-stop
    + + +
    @@ -106,6 +118,9 @@
    +
    +
    +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index f765575..6fd528b 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -217,6 +217,24 @@ button.focus{ #appIcon > div{ background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDYwIDYwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2MCA2MDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPgo8Zz4KCTxwYXRoIGQ9Ik00NS41NjMsMjkuMTc0bC0yMi0xNWMtMC4zMDctMC4yMDgtMC43MDMtMC4yMzEtMS4wMzEtMC4wNThDMjIuMjA1LDE0LjI4OSwyMiwxNC42MjksMjIsMTV2MzAgICBjMCwwLjM3MSwwLjIwNSwwLjcxMSwwLjUzMywwLjg4NEMyMi42NzksNDUuOTYyLDIyLjg0LDQ2LDIzLDQ2YzAuMTk3LDAsMC4zOTQtMC4wNTksMC41NjMtMC4xNzRsMjItMTUgICBDNDUuODM2LDMwLjY0LDQ2LDMwLjMzMSw0NiwzMFM0NS44MzYsMjkuMzYsNDUuNTYzLDI5LjE3NHogTTI0LDQzLjEwN1YxNi44OTNMNDMuMjI1LDMwTDI0LDQzLjEwN3oiIGZpbGw9IiNGRkZGRkYiLz4KCTxwYXRoIGQ9Ik0zMCwwQzEzLjQ1OCwwLDAsMTMuNDU4LDAsMzBzMTMuNDU4LDMwLDMwLDMwczMwLTEzLjQ1OCwzMC0zMFM0Ni41NDIsMCwzMCwweiBNMzAsNThDMTQuNTYxLDU4LDIsNDUuNDM5LDIsMzAgICBTMTQuNTYxLDIsMzAsMnMyOCwxMi41NjEsMjgsMjhTNDUuNDM5LDU4LDMwLDU4eiIgZmlsbD0iI0ZGRkZGRiIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat; } +.cameraIcon{ + background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMCAxMDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4Ij4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNNTAsNDBjLTguMjg1LDAtMTUsNi43MTgtMTUsMTVjMCw4LjI4NSw2LjcxNSwxNSwxNSwxNWM4LjI4MywwLDE1LTYuNzE1LDE1LTE1ICAgIEM2NSw0Ni43MTgsNTguMjgzLDQwLDUwLDQweiBNOTAsMjVINzhjLTEuNjUsMC0zLjQyOC0xLjI4LTMuOTQ5LTIuODQ2bC0zLjEwMi05LjMwOUM3MC40MjYsMTEuMjgsNjguNjUsMTAsNjcsMTBIMzMgICAgYy0xLjY1LDAtMy40MjgsMS4yOC0zLjk0OSwyLjg0NmwtMy4xMDIsOS4zMDlDMjUuNDI2LDIzLjcyLDIzLjY1LDI1LDIyLDI1SDEwQzQuNSwyNSwwLDI5LjUsMCwzNXY0NWMwLDUuNSw0LjUsMTAsMTAsMTBoODAgICAgYzUuNSwwLDEwLTQuNSwxMC0xMFYzNUMxMDAsMjkuNSw5NS41LDI1LDkwLDI1eiBNNTAsODBjLTEzLjgwNywwLTI1LTExLjE5My0yNS0yNWMwLTEzLjgwNiwxMS4xOTMtMjUsMjUtMjUgICAgYzEzLjgwNSwwLDI1LDExLjE5NCwyNSwyNUM3NSw2OC44MDcsNjMuODA1LDgwLDUwLDgweiBNODYuNSw0MS45OTNjLTEuOTMyLDAtMy41LTEuNTY2LTMuNS0zLjVjMC0xLjkzMiwxLjU2OC0zLjUsMy41LTMuNSAgICBjMS45MzQsMCwzLjUsMS41NjgsMy41LDMuNUM5MCw0MC40MjcsODguNDMzLDQxLjk5Myw4Ni41LDQxLjk5M3oiIGZpbGw9IiNGRkZGRkYiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K) no-repeat; +} + +#cameraIcon { + display: none; +} + +body.mobile #cameraIcon{ + display: block; +} + +button i { + display: block; + width: 33px; + height: 33px; + margin: 10px auto; +} footer{ width: 100%; @@ -234,6 +252,10 @@ footer .icon { border-right: 1px solid rgba(255, 255, 255, 0.3); } +body.mobile footer .icon{ + width: 25%; +} + footer .icon:last-child{ border-right: 0; } @@ -309,4 +331,14 @@ footer > div.selected{ } #password.active,#ip.active,#wifi.active{ display: block; +} + +.indicator { + color: red; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + + border-bottom: 5px solid black; } \ No newline at end of file diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index b542bf2..fe3084f 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -217,6 +217,11 @@ var mscriptPage = function () { document.getElementById('mscriptIcon').classList.add('selected'); editor.cm.refresh(); }; +var cameraPage = function () { + unsetPages(); + document.getElementById('camera').classList.add('selected'); + document.getElementById('cameraIcon').classList.add('selected'); +}; var spinnerInit = function () { const spinnerOpts = { lines: 13 // The number of lines to draw diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index c8d605c..1b99792 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -128,6 +128,7 @@ mobile.init = function () { } spinnerInit(); mobile.ble.scan(); + mobile.cameraValues() }; @@ -408,6 +409,76 @@ mobile.setWifiSuccess = function () { setTimeout(mobile.getWifi, 100); }; +mobile.getCamera = function () { + var opts = { + quality: 30, + sourceType: Camera.PictureSourceType.CAMERA, + destinationType: Camera.DestinationType.FILE_URI + }; + navigator.camera.getPicture(mobile.cameraSuccess, mobile.cameraError, opts); +}; +mobile.cameraSuccess = function (result) { + var thisResult = JSON.parse(result); + var metadata = JSON.parse(thisResult.json_metadata); + + mobile.cameraExposure(fstop, metadata); +} +mobile.cameraError = function (err) { + console.error(err); + alert(err); +}; + +mobile.cameraExposure = function (exif) { + var fstop = document.querySelector('.fstop').value || 5.6; + var iso = document.querySelector('.iso').value || 100; + /* + ApertureValue: 1.6959938131099002 + BrightnessValue: -0.3966568568788107 + ColorSpace: 65535 + DateTimeDigitized: "2018:01:08 23:06:13" + DateTimeOriginal: "2018:01:08 23:06:13" + ExposureBiasValue: 0 + ExposureMode: 0 + ExposureProgram: 2 + ExposureTime: 0.2 + FNumber: 1.8 + Flash: 24 + FocalLenIn35mmFilm: 28 + FocalLength: 3.99 + ISOSpeedRatings: [100] (1) + LensMake: "Apple" + LensModel: "iPhone 8 back camera 3.99mm f/1.8" + LensSpecification: [3.99, 3.99, 1.8, 1.8] (4) + MeteringMode: 5 + PixelXDimension: 4032 + PixelYDimension: 3024 + SceneType: 1 + SensingMethod: 2 + ShutterSpeedValue: 2.38401125849867 + SubjectArea: [2015, 1511, 2217, 1330] (4) + SubsecTimeDigitized: "567" + SubsecTimeOriginal: "567" + WhiteBalance: 0 + */ + exif.AperatureValue || exif.FNumber + exif.ExposureTime + exif.ISOSpeedRatings +}; +mobile.cameraValues = function () { + document.querySelectorAll('.iso').forEach(input => { + input.onchange = function () { + var val = this.value; + document.querySelectorAll('.iso').forEach(e => { + e.value = val; + }) + } + }) +} + +/** + * Mobile helper functions + */ + function bytesToString (buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); }; From 71cfaff6c80c37dd63f7d061d8bdd91b616d9eae Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 9 Jan 2018 00:15:30 -0500 Subject: [PATCH 356/414] Install camera with exif plugin --- app/config.xml | 6 +++++- app/install.sh | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/config.xml b/app/config.xml index e0a4bc0..91d0079 100644 --- a/app/config.xml +++ b/app/config.xml @@ -33,12 +33,16 @@ + - + + + + diff --git a/app/install.sh b/app/install.sh index 5551b72..a25ed2e 100644 --- a/app/install.sh +++ b/app/install.sh @@ -7,3 +7,4 @@ cordova plugin add cordova-plugin-device cordova plugin add cordova-plugin-dialogs cordova plugin add cordova-plugin-ble-central --variable BLUETOOTH_USAGE_DESCRIPTION="INTVAL intervalometer controls" cordova plugin add cordova-plugin-statusbar +cordova plugin add cordova-plugin-camera-with-exif \ No newline at end of file From 757e3dd86d723245201ede9d4f8a76d515284430 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 9 Jan 2018 13:39:43 -0500 Subject: [PATCH 357/414] Only list top 5 wifi hotspots --- lib/wifi/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/wifi/index.js b/lib/wifi/index.js index c28a6d8..193f4d7 100644 --- a/lib/wifi/index.js +++ b/lib/wifi/index.js @@ -36,12 +36,14 @@ class Wifi { const lines = stdout.split('\n') let output = [] let line + let i = 0 for (let l of lines) { line = l.replace('ESSID:', '').trim() - if (line !== '""') { + if (line !== '""' && i < 5) { line = line.replace(quoteRe, '') output.push(line) } + i++ } output = output.filter(ap => { if (ap !== '') return ap From ca4282c02f8f69af3cf7b83e561ee17fbb0e37f9 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 9 Jan 2018 15:09:39 -0500 Subject: [PATCH 358/414] Change was causing double frames. Remove for now. --- lib/intval/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/intval/index.js b/lib/intval/index.js index 5bde0de..730fbb4 100644 --- a/lib/intval/index.js +++ b/lib/intval/index.js @@ -239,7 +239,7 @@ intval._watchMicro = function (err, val) { //log.info(`Microswitch val: ${val}`) //determine when to stop if (val === 0 && intval._state.frame.active) { - if (!intval._state.micro.primed && (now - intval._state.frame.start) > 100) { + if (!intval._state.micro.primed) { intval._state.micro.primed = true intval._state.micro.time = now log.info('Microswitch primed to stop motor') From 64adc6cc86de490dbd4f14b6fef1f01b6d8d2ae2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Thu, 11 Jan 2018 15:50:36 -0500 Subject: [PATCH 359/414] Create an update script and add an update command to the main process. --- index.js | 5 +++++ scripts/update.sh | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 scripts/update.sh diff --git a/index.js b/index.js index 2937f23..02b217d 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ const restify = require('restify') const log = require('./lib/log')('main') const fs = require('fs') +const { exec } = require('child_process') const BLE = require('./lib/ble') const intval = require('./lib/intval') @@ -519,6 +520,10 @@ function seq () { } } +function update (req, res, next) { + exec('sh ./scripts/update.sh') +} + function index (req, res, next) { fs.readFile(INDEXPATH, 'utf8', (err, data) => { if (err) { diff --git a/scripts/update.sh b/scripts/update.sh new file mode 100644 index 0000000..db6f8b5 --- /dev/null +++ b/scripts/update.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd /home/pi/intval3 + +sudo -u pi -i<< EOF + git pull +EOF + +sudo pm2 restart process.json \ No newline at end of file From 66b3711118f8145921f044a9e45662c89d9d13a1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 12 Jan 2018 17:46:54 -0500 Subject: [PATCH 360/414] Retry update.sh --- scripts/update.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/update.sh b/scripts/update.sh index db6f8b5..005dee0 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -1,9 +1,7 @@ #!/bin/bash -cd /home/pi/intval3 - sudo -u pi -i<< EOF - git pull + cd /home/pi/intval3 && git pull EOF -sudo pm2 restart process.json \ No newline at end of file +cd /home/pi/intval3 && sudo pm2 restart process.json \ No newline at end of file From 63eee43386b0e78bcd98301ff1fc192b9301eca1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 12 Jan 2018 17:48:26 -0500 Subject: [PATCH 361/414] Simply exit process to restart. --- index.js | 4 +++- scripts/update.sh | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 02b217d..80fcbd8 100644 --- a/index.js +++ b/index.js @@ -521,7 +521,9 @@ function seq () { } function update (req, res, next) { - exec('sh ./scripts/update.sh') + exec('sh ./scripts/update.sh', (err, stdio, stderr) => { + process.exit() + }) } function index (req, res, next) { diff --git a/scripts/update.sh b/scripts/update.sh index 005dee0..a866bc5 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -2,6 +2,4 @@ sudo -u pi -i<< EOF cd /home/pi/intval3 && git pull -EOF - -cd /home/pi/intval3 && sudo pm2 restart process.json \ No newline at end of file +EOF \ No newline at end of file From b8787c0fac8c0c8eabf2da109afdb264b08334de Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 17 Jan 2018 15:58:55 -0500 Subject: [PATCH 362/414] If exposure if over 5000ms, call back from ble service immediately and block UI until frame is done. --- app/www/static/js/intval.mobile.js | 14 +++++++++++--- index.js | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 1b99792..c610fd2 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -171,9 +171,17 @@ mobile.frame = function () { mobile.frameSuccess = function () { console.log('Frame finished, getting state.'); - mobile.ble.active = false; - document.getElementById('frame').classList.remove('focus'); - mobile.getState(); + if (STATE.exposure < 5000) { + mobile.ble.active = false; + document.getElementById('frame').classList.remove('focus'); + mobile.getState(); + } else { + setTimeout(() => { + mobile.ble.active = false; + document.getElementById('frame').classList.remove('focus'); + mobile.getState(); + }, STATE.exposure + 500) + } } mobile.setDir = function () { const opts = { diff --git a/index.js b/index.js index 80fcbd8..2f7a3ca 100644 --- a/index.js +++ b/index.js @@ -363,9 +363,16 @@ function bFrame (obj, cb) { } } log.info('frame', { method : 'ble', dir : dir, exposure : exposure }) - intval.frame(dir, exposure, (len) => { + + if (exposure < 5000) { + intval.frame(dir, exposure, (len) => { + return cb() + }) + } else { + intval.frame(dir, exposure, (len) => {}) return cb() - }) + } + //setTimeout(cb, exposure === 0 ? 630 : exposure) } @@ -485,6 +492,10 @@ function bSequence (obj, cb) { } } +function bSequenceStop (obj, cb) { + +} + function bReset (obj, cb) { intval.reset() setTimeout(cb, 10) From fc4b180aa8231ce6c74a3dbad5f1607d858aae00 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 17 Jan 2018 17:20:48 -0500 Subject: [PATCH 363/414] Fix logging in delayed state retreival for ble connections. --- app/www/static/js/intval.mobile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index c610fd2..aff777c 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -170,13 +170,14 @@ mobile.frame = function () { mobile.frameSuccess = function () { - console.log('Frame finished, getting state.'); if (STATE.exposure < 5000) { + console.log('Frame finished, getting state.'); mobile.ble.active = false; document.getElementById('frame').classList.remove('focus'); mobile.getState(); } else { setTimeout(() => { + console.log('Frame finished, getting state.'); mobile.ble.active = false; document.getElementById('frame').classList.remove('focus'); mobile.getState(); From 286c0eec4cb278f16e487c3b58e6d295d19bb2b3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 2 Feb 2018 13:02:15 -0500 Subject: [PATCH 364/414] Pull mscript features for now --- app/www/index.html | 16 ++++++++-------- app/www/static/css/index.css | 10 +++++++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index a9aeac4..500d7ca 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -5,8 +5,8 @@ - - + @@ -94,12 +94,12 @@
    -
    +

    CAMERA EXPOSURE

    ISO
    @@ -115,20 +115,20 @@
    -
    +
    - + - + \ No newline at end of file diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 6fd528b..8e3ab64 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -245,7 +245,8 @@ footer{ } footer .icon { - width: 33.33%; + /*width: 33.33%;*/ + width: 50%; height: 50px; float: left; box-sizing: border-box; @@ -253,7 +254,8 @@ footer .icon { } body.mobile footer .icon{ - width: 25%; + /*width: 25%;*/ + width: 33.33%; } footer .icon:last-child{ @@ -329,7 +331,9 @@ footer > div.selected{ #password,#wifi,#ip{ display: none; } -#password.active,#ip.active,#wifi.active{ +#password.active, +#ip.active, +#wifi.active{ display: block; } From 4760c1e49194d11e407734932c89623d03f9eab9 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 2 Feb 2018 15:36:28 -0500 Subject: [PATCH 365/414] Sync camera settings between multiple inputs and write to settings --- app/www/index.html | 54 ++++++++++++++++++++++---------- app/www/static/css/index.css | 19 ++++++++++- app/www/static/js/index.js | 2 ++ app/www/static/js/intval.core.js | 31 ++++++++++++++++++ 4 files changed, 88 insertions(+), 18 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 500d7ca..6e8e2eb 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -9,7 +9,7 @@ --> - +
    @@ -57,17 +57,23 @@

    SETTINGS

    -
    ISO
    - -
    F-stop
    - -
    -
    Rex-o-fader
    - +
    +
    +
    ISO
    + +
    +
    +
    F-stop
    + +
    +
    +
    Rex-o-fader
    + +

    BLUETOOTH

    @@ -102,11 +108,25 @@
    -->

    CAMERA EXPOSURE

    -
    ISO
    - -
    F-stop
    - - +
    +
    +
    ISO
    + +
    +
    +
    F-stop
    + +
    +
    +
    Rex-o-fader
    + +
    +
    +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 8e3ab64..7f2977e 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -36,6 +36,13 @@ html,body,button,h2,label,input{ font-family: 'Arial Neue', Helvetical, Arial, sans-serif; } + +.clearfix::after { + content: ""; + clear: both; + display: table; +} + body.mobile{ padding-top: 35px; } @@ -336,7 +343,6 @@ footer > div.selected{ #wifi.active{ display: block; } - .indicator { color: red; width: 0; @@ -345,4 +351,15 @@ footer > div.selected{ border-right: 5px solid transparent; border-bottom: 5px solid black; +} +.setting{ + width: 50%; + float: left; +} +.setting input, +.setting select{ + width: 90%; +} +#camera{ + margin-top: 20px; } \ No newline at end of file diff --git a/app/www/static/js/index.js b/app/www/static/js/index.js index 1c8de02..015c8be 100644 --- a/app/www/static/js/index.js +++ b/app/www/static/js/index.js @@ -23,6 +23,7 @@ var app = { document.addEventListener('resume', this.onDeviceResume.bind(this), false); document.addEventListener('DOMContentLoaded', event => { if (typeof cordova === 'undefined') { + init(); web.init(); getState(); } @@ -34,6 +35,7 @@ var app = { // Bind any cordova events here. Common events are: // 'pause', 'resume', etc. onDeviceReady: function() { + init(); mobile.init(); }, onDeviceResume : function () { diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index fe3084f..3556531 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -2,6 +2,8 @@ const BOLEX = { angle : 133, prism : 0.8, + iso : 100, + fstop : 5.6, expected : 630 }; const STATE = { @@ -201,6 +203,23 @@ var seqState = function (state) { } }; +var syncInputs = function (selector, cb) { + const elems = document.querySelectorAll(selector); + [].forEach.call(elems, function (input) { + input.oninput = function () { + setInputs(selector, this.value) + cb(this.value) + } + }); +}; + +var setInputs = function (selector, value) { + const elems = document.querySelectorAll(selector); + [].forEach.call(elems, function (input) { + input.value = value; + }); +}; + var appPage = function () { unsetPages(); document.getElementById('app').classList.add('selected'); @@ -262,4 +281,16 @@ var spinnerHide = function () { } var isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); +}; + +var init = function () { + syncInputs('.angle', (val) => { + BOLEX.angle = parseInt(val); + }); + syncInputs('.iso', (val) => { + BOLEX.iso = parseInt(val); + }); + syncInputs('.fstop', (val) => { + BOLEX.fstop = parseFloat(val); + }); }; \ No newline at end of file From 3abd68f5f3900c38773add8383a0a39f7faf3744 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 2 Feb 2018 15:36:50 -0500 Subject: [PATCH 366/414] Dump exif data after taking picture --- app/www/static/js/intval.mobile.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index aff777c..33a4df3 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -111,8 +111,8 @@ mobile.ble.onError = function (err) { }; mobile.init = function () { - const bleInputs = document.querySelectorAll('.ble') - document.querySelector('body').classList.add('mobile') + const bleInputs = document.querySelectorAll('.ble'); + document.querySelector('body').classList.add('mobile'); window.frame = mobile.frame; window.getState = mobile.getState; @@ -419,7 +419,7 @@ mobile.setWifiSuccess = function () { }; mobile.getCamera = function () { - var opts = { + const opts = { quality: 30, sourceType: Camera.PictureSourceType.CAMERA, destinationType: Camera.DestinationType.FILE_URI @@ -427,8 +427,8 @@ mobile.getCamera = function () { navigator.camera.getPicture(mobile.cameraSuccess, mobile.cameraError, opts); }; mobile.cameraSuccess = function (result) { - var thisResult = JSON.parse(result); - var metadata = JSON.parse(thisResult.json_metadata); + const thisResult = JSON.parse(result); + const metadata = JSON.parse(thisResult.json_metadata); mobile.cameraExposure(fstop, metadata); } @@ -438,8 +438,14 @@ mobile.cameraError = function (err) { }; mobile.cameraExposure = function (exif) { - var fstop = document.querySelector('.fstop').value || 5.6; - var iso = document.querySelector('.iso').value || 100; + const fstop = BOLEX.fstop || 5.6; + const iso = BOLEX.iso || 100; + + const cFstop = exif.AperatureValue || exif.FNumber; + const cExposure = exif.ShutterSpeedValue ? (1 / exif.ShutterSpeedValue) * 1000 : exif.ExposureTime * 1000; + const cISO = exif.ISOSpeedRatings[0]; + + alert(`${fstop} ${iso} ${cFstop} ${cISO} ${cExposure}`); /* ApertureValue: 1.6959938131099002 BrightnessValue: -0.3966568568788107 @@ -469,9 +475,6 @@ mobile.cameraExposure = function (exif) { SubsecTimeOriginal: "567" WhiteBalance: 0 */ - exif.AperatureValue || exif.FNumber - exif.ExposureTime - exif.ISOSpeedRatings }; mobile.cameraValues = function () { document.querySelectorAll('.iso').forEach(input => { From 7a392aba0e92edb8985fdca3ba3a32e5c26b7a23 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 16:46:32 -0500 Subject: [PATCH 367/414] Bold title on main screen --- app/www/static/css/index.css | 44 +++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 7f2977e..1ff4e98 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -44,7 +44,7 @@ html,body,button,h2,label,input{ } body.mobile{ - padding-top: 35px; + padding-top: 5px; } .page{ padding: 5px 10% 0 10%; @@ -192,6 +192,16 @@ button.focus{ margin-top: 6px; margin-bottom: 9px; } + +/* MAIN */ +#app{ + +} + +#app > h2{ + font-weight: bold; +} + #exposure, #delay{ width: 70%; @@ -360,6 +370,34 @@ footer > div.selected{ .setting select{ width: 90%; } -#camera{ +.setting .label{ + margin-bottom: 3px; +} +#cameraBtn{ margin-top: 20px; -} \ No newline at end of file +} +#camera_exposure, +#bolex_exposure{ + width: 50%; + float: left; +} +#camera_exposure input, +#bolex_exposure input{ + margin: 12px auto; + width: 69%; + display: block; +} +#camera_exposure h3, +#bolex_exposure h3{ + text-align: center; + font-weight: normal; +} +#camera_exposure div label, +#bolex_exposure div label{ + color: #666; + position: absolute; + margin-top: 8px; + text-align: center; + width: 100%; + margin-left: -50%; +} From 4b72c9eeed2b21da293728fc29a7d7c431def676 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 16:49:37 -0500 Subject: [PATCH 368/414] Camera exposure logic is in place. Will take a photo with the device camera and use the exif data to determine the correct exposure for the bolex. - Determines # of stops between fstop of phone and set fstop of bolex - Determines # of stops between iso of phone and set iso of bolex - Compensates for the 0.8 prism constant --- app/www/static/js/intval.mobile.js | 238 +++++++++++++++++++++-------- 1 file changed, 178 insertions(+), 60 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 33a4df3..79b1abf 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -1,3 +1,7 @@ +/* jshint esversion:6, strict:true, browser:true*/ +/* global console, alert */ + + 'use strict'; var mobile = {}; @@ -29,7 +33,7 @@ mobile.ble.scan = function () { alert('No INTVAL devices found.'); settingsPage(); } - }, 5000) + }, 5000); }; mobile.ble.onDiscover = function (device) { @@ -43,18 +47,18 @@ mobile.ble.onDiscover = function (device) { } else { //console.log(`BLE - Discovered Other ${device.id}`); } -} +}; mobile.ble.connect = function (device) { - console.log(`BLE - Connecting to ${device.id}`) + console.log(`BLE - Connecting to ${device.id}`); ble.connect(device.id, (peripheral) => { mobile.ble.onConnect(peripheral, device); }, mobile.ble.onError); }; mobile.ble.onConnect = function (peripheral, device) { - const elem = document.getElementById('bluetooth') - const option = document.createElement('option') + const elem = document.getElementById('bluetooth'); + const option = document.createElement('option'); const disconnect = document.getElementById('disconnect'); const scan = document.getElementById('scan'); @@ -85,11 +89,11 @@ mobile.ble.disconnect = function () { const scan = document.getElementById('scan'); let device; if (!mobile.ble.connected) { - console.warn('Not connected to any device') - return false + console.warn('Not connected to any device'); + return false; } - device = mobile.ble.device - console.log(`BLE - Disconnecting from ${device.id}`) + device = mobile.ble.device; + console.log(`BLE - Disconnecting from ${device.id}`); ble.disconnect(device.id, mobile.ble.onDisconnect, mobile.ble.onDisconnect); elem.innerHTML = ''; @@ -112,6 +116,9 @@ mobile.ble.onError = function (err) { mobile.init = function () { const bleInputs = document.querySelectorAll('.ble'); + const bolIso = document.querySelector('.iso'); + const bolF = document.querySelector('.fstop'); + document.querySelector('body').classList.add('mobile'); window.frame = mobile.frame; @@ -128,13 +135,13 @@ mobile.init = function () { } spinnerInit(); mobile.ble.scan(); - mobile.cameraValues() + mobile.cameraValues(); }; mobile.getState = function () { if (!mobile.ble.connected) { - // + //returning here will prevent error alert } ble.read(mobile.ble.device.id, mobile.ble.SERVICE_ID, @@ -145,7 +152,7 @@ mobile.getState = function () { mobile.stateSuccess = function (data) { let str = bytesToString(data); let res = JSON.parse(str); - setState(res) + setState(res); }; mobile.frame = function () { @@ -229,7 +236,7 @@ mobile.exposureSuccess = function () { mobile.setDelay = function () { const delay = document.getElementById('delay').value; - const scaledDelay = scaleTime(delay, STATE.delayScale) + const scaledDelay = scaleTime(delay, STATE.delayScale); let opts = { type : 'delay', delay : scaledDelay @@ -243,7 +250,7 @@ mobile.setDelay = function () { } mobile.delaySuccess = function () { - console.log('Set delay') + console.log('Set delay'); mobile.getState(); }; @@ -417,6 +424,7 @@ mobile.setWifiSuccess = function () { console.log('Set new wifi credentials'); setTimeout(mobile.getWifi, 100); }; +mobile.exif = {} mobile.getCamera = function () { const opts = { @@ -430,62 +438,171 @@ mobile.cameraSuccess = function (result) { const thisResult = JSON.parse(result); const metadata = JSON.parse(thisResult.json_metadata); - mobile.cameraExposure(fstop, metadata); -} + mobile.cameraExposure(metadata.Exif); +}; mobile.cameraError = function (err) { console.error(err); alert(err); }; mobile.cameraExposure = function (exif) { - const fstop = BOLEX.fstop || 5.6; + const cam_exp = document.getElementById('cam_exp'); + const cam_f = document.getElementById('cam_f'); + const cam_iso = document.getElementById('cam_iso'); + const bol_exp = document.getElementById('bol_exp'); + const bol_f = document.getElementById('bol_f'); + const bol_iso = document.getElementById('bol_iso'); + const bol_f_diff = document.getElementById('bol_f_diff'); + const bol_iso_diff = document.getElementById('bol_iso_diff'); + const bol_exp_diff = document.getElementById('bol_exp_diff'); + + const fstop = BOLEX.fstop || 5.6; const iso = BOLEX.iso || 100; + const prism = BOLEX.prism || 0.8; - const cFstop = exif.AperatureValue || exif.FNumber; - const cExposure = exif.ShutterSpeedValue ? (1 / exif.ShutterSpeedValue) * 1000 : exif.ExposureTime * 1000; - const cISO = exif.ISOSpeedRatings[0]; + const cFstop = exif.ApertureValue || exif.FNumber; + const cExposure = exif.ExposureTime * 1000; + const cIso = exif.ISOSpeedRatings[0]; + + //convert fstop to "fnumber", an absolute scale where stops are scaled to 1.0 + const f = mobile.fnumber(cFstop); + const target = mobile.fnumber(fstop); //bolex + + let exposure = cExposure; + let isoStops = 0; + let fStops = 0; + let scale_elem; + let exposure_elem; + let proceed; + let e1; + let e2; + + mobile.exif = exif; + console.dir(exif); + + console.log(`fstop : ${fstop}`); + console.log(`iso : ${iso}`); + bol_f.value = fstop; + bol_iso.value = iso; + + console.log(`cExposure : ${cExposure}`); + console.log(`cFstop : ${cFstop}`); + console.log(`cIso : ${cIso}`); + cam_exp.value = cExposure; + cam_f.value = cFstop; + cam_iso.value = cIso; + + console.log(`f : ${f}`); + console.log(`target : ${target}`); + + //Determine if fstop of phone camera "f" + if (target !== f) { + fStops = f - target; + exposure = exposure / Math.pow(2, fStops); + } + bol_f_diff.innerHTML = Math.round(parseFloat(-fStops) * 10) / 10; + console.log(`fstops : ${fStops}`); + console.log(`exposure => ${exposure}`); + + if (cIso != iso) { + isoStops = (Math.log(cIso) / Math.log(2)) - (Math.log(iso) / Math.log(2)); + } + bol_iso_diff.innerHTML = Math.round(parseFloat(isoStops) * 10) / 10; + console.log(`isoStops : ${isoStops}`) + + //Double or halve exposure based on the differences in ISO stops + exposure = exposure * Math.pow(2, isoStops); + console.log(`exposure => ${exposure}`); + + console.log(`prism : ${prism}`); + //Compensate for Bolex prism + exposure = exposure * Math.pow(2, prism); + console.log(`exposure => ${prism}`); + + exposure = Math.round(exposure) //round to nearest millisecond + bol_exp.value = exposure; + bol_exp_diff.value = 0; + + if (exposure > 500) { + proceed = confirm(`Set camera exposure to ${exposure}ms to match photo.`); + } + + if (proceed && exposure > 500) { + e1 = new Event('change'); + e2 = new Event('change'); + + scale_elem = document.getElementById('scale'); + exposure_elem = document.getElementById('exposure'); + + scale_elem.value = 'ms'; + scale_elem.dispatchEvent(e1); + + exposure_elem.value = exposure; + exposure_elem.dispatchEvent(e2); + } - alert(`${fstop} ${iso} ${cFstop} ${cISO} ${cExposure}`); /* - ApertureValue: 1.6959938131099002 - BrightnessValue: -0.3966568568788107 - ColorSpace: 65535 - DateTimeDigitized: "2018:01:08 23:06:13" - DateTimeOriginal: "2018:01:08 23:06:13" - ExposureBiasValue: 0 - ExposureMode: 0 - ExposureProgram: 2 - ExposureTime: 0.2 - FNumber: 1.8 - Flash: 24 - FocalLenIn35mmFilm: 28 - FocalLength: 3.99 - ISOSpeedRatings: [100] (1) - LensMake: "Apple" - LensModel: "iPhone 8 back camera 3.99mm f/1.8" - LensSpecification: [3.99, 3.99, 1.8, 1.8] (4) - MeteringMode: 5 - PixelXDimension: 4032 - PixelYDimension: 3024 - SceneType: 1 - SensingMethod: 2 - ShutterSpeedValue: 2.38401125849867 - SubjectArea: [2015, 1511, 2217, 1330] (4) - SubsecTimeDigitized: "567" - SubsecTimeOriginal: "567" - WhiteBalance: 0 +{ + "Exif": { + "DateTimeOriginal": "2018:02:02 16:59:13", + "ExposureBiasValue": 0, + "SensingMethod": 2, + "BrightnessValue": -0.9969016228800144, + "LensMake": "Apple", + "FNumber": 1.8, + "FocalLength": 3.99, + "ShutterSpeedValue": 2.049355412374274, + "SceneType": 1, + "ApertureValue": 1.6959938131099002, + "SubjectArea": [ + 2015, + 1511, + 2217, + 1330 + ], + "ColorSpace": 65535, + "LensSpecification": [ + 3.99, + 3.99, + 1.8, + 1.8 + ], + "PixelYDimension": 3024, + "WhiteBalance": 0, + "DateTimeDigitized": "2018:02:02 16:59:13", + "ExposureMode": 0, + "ISOSpeedRatings": [ + 100 + ], + "PixelXDimension": 4032, + "LensModel": "iPhone 8 back camera 3.99mm f/1.8", + "ExposureTime": 0.25, + "Flash": 24, + "SubsecTimeDigitized": "209", + "SubsecTimeOriginal": "209", + "ExposureProgram": 2, + "FocalLenIn35mmFilm": 28, + "MeteringMode": 5 + } +} */ }; -mobile.cameraValues = function () { - document.querySelectorAll('.iso').forEach(input => { - input.onchange = function () { - var val = this.value; - document.querySelectorAll('.iso').forEach(e => { - e.value = val; - }) - } - }) -} + +mobile.refreshExposure = function () { + if (typeof mobile.exif.ExposureTime !== 'undefined') { + mobile.cameraExposure(mobile.exif); + } +}; + +mobile.fnumber = function (fstop) { + return Math.log(fstop) / Math.log(Math.sqrt(2)); +}; + +mobile.EV = function (fstop, shutter) { + const sec = shutter / 1000; //shutter in ms => seconds + const square = Math.pow(fstop, 2); + return Math.log(square / sec); +}; /** * Mobile helper functions @@ -493,11 +610,12 @@ mobile.cameraValues = function () { function bytesToString (buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); -}; +} + function stringToBytes(string) { var array = new Uint8Array(string.length); for (var i = 0, l = string.length; i < l; i++) { array[i] = string.charCodeAt(i); } return array.buffer; -}; \ No newline at end of file +} \ No newline at end of file From 29485137caf9900d90e86388602558b64caae2c0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 16:50:14 -0500 Subject: [PATCH 369/414] Store iso and fstop as they are set. --- app/www/static/js/intval.core.js | 38 +++++++++----------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 3556531..a3dae45 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -203,23 +203,6 @@ var seqState = function (state) { } }; -var syncInputs = function (selector, cb) { - const elems = document.querySelectorAll(selector); - [].forEach.call(elems, function (input) { - input.oninput = function () { - setInputs(selector, this.value) - cb(this.value) - } - }); -}; - -var setInputs = function (selector, value) { - const elems = document.querySelectorAll(selector); - [].forEach.call(elems, function (input) { - input.value = value; - }); -}; - var appPage = function () { unsetPages(); document.getElementById('app').classList.add('selected'); @@ -278,19 +261,20 @@ var spinnerHide = function () { if (elem.classList.contains('active')) { elem.classList.remove('active'); } -} +}; var isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); }; + var init = function () { - syncInputs('.angle', (val) => { - BOLEX.angle = parseInt(val); - }); - syncInputs('.iso', (val) => { - BOLEX.iso = parseInt(val); - }); - syncInputs('.fstop', (val) => { - BOLEX.fstop = parseFloat(val); - }); + document.querySelector('.angle').oninput = function () { + BOLEX.angle = parseInt(this.value); + }; + document.querySelector('.iso').oninput = function () { + BOLEX.iso = parseInt(this.value); + }; + document.querySelector('.fstop').oninput = function () { + BOLEX.fstop = parseFloat(this.value); + }; }; \ No newline at end of file From f2ca374e78bb0f89ca007f4c50053cfeffcca843 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 16:52:59 -0500 Subject: [PATCH 370/414] Display readonly camera values for the camera exposure screen. --- app/www/index.html | 67 ++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index 6e8e2eb..e7dd9a5 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -56,25 +56,6 @@
    -

    SETTINGS

    -
    -
    -
    ISO
    - -
    -
    -
    F-stop
    - -
    -
    -
    Rex-o-fader
    - -
    -

    BLUETOOTH

    +
    F-stop
    - +
    Rex-o-fader
    @@ -126,7 +107,47 @@
    - + +
    +
    +

    PHONE

    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    +

    BOLEX

    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    From 2240a432f61b5b69c6598011a21879ec5e4ffbcd Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 17:49:41 -0500 Subject: [PATCH 371/414] Increment build number --- app/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config.xml b/app/config.xml index 91d0079..000b3b6 100644 --- a/app/config.xml +++ b/app/config.xml @@ -1,5 +1,5 @@ - + INTVAL3 Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras From 85a529c70551aea7fb7f062c82203c408194821a Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sat, 3 Feb 2018 17:49:58 -0500 Subject: [PATCH 372/414] Increment build number in package.json --- app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/package.json b/app/package.json index 18eaf29..f9242b2 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "com.sixteenmillimeter.intval3", "displayName": "INTVAL3", - "version": "1.0.2", + "version": "1.0.3", "description": "Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras", "main": "index.js", "scripts": { From df578bb678d6b5519bb63bf4ec94da360cb86fd3 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Sun, 4 Feb 2018 18:17:36 -0500 Subject: [PATCH 373/414] Make mobile.fnumber a global helper function fnumber() --- app/www/static/js/intval.mobile.js | 77 +++++++++++++++++------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index 79b1abf..ef002e1 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -465,63 +465,55 @@ mobile.cameraExposure = function (exif) { const cIso = exif.ISOSpeedRatings[0]; //convert fstop to "fnumber", an absolute scale where stops are scaled to 1.0 - const f = mobile.fnumber(cFstop); - const target = mobile.fnumber(fstop); //bolex + const f = fnumber(cFstop); + const target = fnumber(fstop); //bolex let exposure = cExposure; let isoStops = 0; let fStops = 0; + let expDiff; + let scale_elem; let exposure_elem; + let proceed; let e1; let e2; mobile.exif = exif; - console.dir(exif); - - console.log(`fstop : ${fstop}`); - console.log(`iso : ${iso}`); - bol_f.value = fstop; - bol_iso.value = iso; - - console.log(`cExposure : ${cExposure}`); - console.log(`cFstop : ${cFstop}`); - console.log(`cIso : ${cIso}`); - cam_exp.value = cExposure; - cam_f.value = cFstop; - cam_iso.value = cIso; - - console.log(`f : ${f}`); - console.log(`target : ${target}`); //Determine if fstop of phone camera "f" if (target !== f) { fStops = f - target; exposure = exposure / Math.pow(2, fStops); } - bol_f_diff.innerHTML = Math.round(parseFloat(-fStops) * 10) / 10; - console.log(`fstops : ${fStops}`); - console.log(`exposure => ${exposure}`); if (cIso != iso) { isoStops = (Math.log(cIso) / Math.log(2)) - (Math.log(iso) / Math.log(2)); } - bol_iso_diff.innerHTML = Math.round(parseFloat(isoStops) * 10) / 10; - console.log(`isoStops : ${isoStops}`) //Double or halve exposure based on the differences in ISO stops exposure = exposure * Math.pow(2, isoStops); - console.log(`exposure => ${exposure}`); - console.log(`prism : ${prism}`); //Compensate for Bolex prism exposure = exposure * Math.pow(2, prism); - console.log(`exposure => ${prism}`); exposure = Math.round(exposure) //round to nearest millisecond + + bol_f.value = fstop; + bol_iso.value = iso; bol_exp.value = exposure; - bol_exp_diff.value = 0; + + //Total difference in exposure from phone camera to Bolex + expDiff = (Math.log(exposure) / Math.log(2)) - (Math.log(cExposure) / Math.log(2)); + + bol_exp_diff.innerHTML = floatDisplay(expDiff); + bol_iso_diff.innerHTML = floatDisplay(isoStops); + bol_f_diff.innerHTML = floatDisplay(-fStops); + + cam_exp.value = cExposure; + cam_f.value = cFstop; + cam_iso.value = cIso; if (exposure > 500) { proceed = confirm(`Set camera exposure to ${exposure}ms to match photo.`); @@ -594,10 +586,6 @@ mobile.refreshExposure = function () { } }; -mobile.fnumber = function (fstop) { - return Math.log(fstop) / Math.log(Math.sqrt(2)); -}; - mobile.EV = function (fstop, shutter) { const sec = shutter / 1000; //shutter in ms => seconds const square = Math.pow(fstop, 2); @@ -618,4 +606,29 @@ function stringToBytes(string) { array[i] = string.charCodeAt(i); } return array.buffer; -} \ No newline at end of file +} + +function fnumber (fstop) { + return Math.log(fstop) / Math.log(Math.sqrt(2)); +} + +function floatDisplay (value) { + let str = value + ''; + const period = str.indexOf('.'); + if (period === -1) { + str = str + '.0'; + } else { + str = roundTenth(value) + ''; + } + if (value < 0) { + str = `${(str + '')}`; + } else if (value > 0) { + str = `+${(str + '')}`; + } + return str; +} + +function roundTenth (value) { + return Math.round((value * 10) / 10) +} + From b5189a53eb65b913d8350a82a058dcb2a78bbb72 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 6 Feb 2018 17:34:04 -0500 Subject: [PATCH 374/414] Pull amazon links --- Readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index d777fe6..eab400d 100644 --- a/Readme.md +++ b/Readme.md @@ -20,9 +20,9 @@ The [INTVAL2](https://github.com/sixteenmillimeter/intval2) project should be us ## PARTS -1. Raspberry Pi Zero W - [[Amazon](http://amzn.to/2BWkKAy)] [[Adafruit](https://www.adafruit.com/product/3400)] [[Sparkfun](https://www.sparkfun.com/products/14277)] -2. L298N Breakout Board - [[Amazon](http://amzn.to/2DwBrmz)] -3. 120RPM 12VDC Motor - [[Amazon](http://amzn.to/2CbRw4R)] -4. Microswitch w/ Roller - [[Amazon](http://amzn.to/2CdG4FW)] [[Adafruit](https://www.adafruit.com/product/819)] -5. L7805 5V Regulator - [[Amazon](http://amzn.to/2CeFNlZ)] [[Adafruit](https://www.adafruit.com/product/2164)] [Sparkfun](https://www.sparkfun.com/products/107)] -6. (Optional) Proto Bonnet - [[Amazon](http://amzn.to/2ChnSLm)] [Adafruit](https://www.adafruit.com/product/3203) \ No newline at end of file +1. Raspberry Pi Zero W - [[Adafruit](https://www.adafruit.com/product/3400)] [[Sparkfun](https://www.sparkfun.com/products/14277)] +2. L298N Breakout Board - ? +3. 120RPM 12VDC Motor - ? +4. Microswitch w/ Roller - [[Adafruit](https://www.adafruit.com/product/819)] +5. L7805 5V Regulator - [[Adafruit](https://www.adafruit.com/product/2164)] [Sparkfun](https://www.sparkfun.com/products/107)] +6. (Optional) Proto Bonnet - [[Adafruit](https://www.adafruit.com/product/3203)] \ No newline at end of file From 4e69871d3820d959f96bc23593ec01787c361b7d Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 6 Feb 2018 17:34:58 -0500 Subject: [PATCH 375/414] Pull default mobile styles. --- app/www/index.html | 4 ++-- app/www/static/css/index.css | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/www/index.html b/app/www/index.html index e7dd9a5..35b6af6 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -9,7 +9,7 @@ --> - +
    @@ -144,7 +144,7 @@
    - +
    diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 1ff4e98..8866890 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -386,6 +386,7 @@ footer > div.selected{ margin: 12px auto; width: 69%; display: block; + border-color: #666; } #camera_exposure h3, #bolex_exposure h3{ @@ -401,3 +402,9 @@ footer > div.selected{ width: 100%; margin-left: -50%; } +span.neg{ + color: #f32121;; +} +span.pos{ + color: #20ce45; +} From 9a698f6bd608c23136a160f502457f7efd4b4ad1 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 6 Feb 2018 17:37:49 -0500 Subject: [PATCH 376/414] Hide footer menu unless inside mobile app, ignore on web. --- app/www/static/css/index.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 8866890..c660110 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -254,6 +254,7 @@ button i { } footer{ + display: none; width: 100%; height: 50px; position: fixed; @@ -408,3 +409,6 @@ span.neg{ span.pos{ color: #20ce45; } +body.mobile footer{ + display: block; +} From 8ec1694b02a09453870643aef4ea6f540b5c6d86 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 6 Feb 2018 17:42:50 -0500 Subject: [PATCH 377/414] Add a "sequence stop" endpoint --- index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 2f7a3ca..9abde59 100644 --- a/index.js +++ b/index.js @@ -54,6 +54,7 @@ function createBLE () { ble.on('delay', bDelay) ble.on('counter', bCounter) ble.on('sequence', bSequence) + ble.on('stop', bSequenceStop) ble.on('reset', bReset) } @@ -465,6 +466,7 @@ function bSequence (obj, cb) { } } if (intval._state.sequence && sequence._state.active) { + //should not occur with single client sequence.setStop() intval._state.sequence = false log.info('sequence stop', { method : 'ble' }) @@ -493,7 +495,13 @@ function bSequence (obj, cb) { } function bSequenceStop (obj, cb) { - + // + if (intval._state.sequence && sequence._state.active) { + sequence.setStop() + intval._state.sequence = false + log.info('sequence stop', { method : 'ble' }) + return cb() + } } function bReset (obj, cb) { From e8eec9510723aee8122dec5aacf1c16caf7db5c0 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 7 Feb 2018 18:22:28 -0500 Subject: [PATCH 378/414] Create both readme files, one at the project level and another just for the mobile app. More work needed on the project one. --- Readme.md | 31 +++++++++++++++++++++++++++++-- app/Readme.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 app/Readme.md diff --git a/Readme.md b/Readme.md index eab400d..f0ab4f0 100644 --- a/Readme.md +++ b/Readme.md @@ -10,12 +10,39 @@ The [INTVAL2](https://github.com/sixteenmillimeter/intval2) project should be us ### Components -* Firmware for the Raspberry Pi Zero W running on [Node.js](https://nodejs.org) -* Mobile/Web/API for controlling device, using [Cordova](https://cordova.apache.org/) + [Bleno](https://github.com/sandeepmistry/bleno), and [Restify](http://restify.com/) +* [Firmware](#firmware) for the Raspberry Pi Zero W running [Node.js](https://nodejs.org) on Raspian +* [Mobile app](#mobile) for controlling device using [Cordova](https://cordova.apache.org/) + [Bleno](https://github.com/sandeepmistry/bleno) +* [Web app](#web) for controlling device using [Restify](http://restify.com/) * Hardware files, parts models for 3D printing, laser cutting and CNC * PCB design for a Raspberry Pi Zero W Bonnet * [Parts list](#parts-list) + + +## Firmware + +The firmware of the INTVAL3 is a node.js application running on the Raspian OS intended for installation on the Raspberry Pi Zero W. + + + +## Mobile App + +The INTVAL3 mobile app controls the intervalometer over Bluetooth. It can be used to configure the settings on the intervalometer such as exposure length, delay between frames and the direction of the film. The app can also be used to trigger individual frames, as well as start and stop sequences. As an experimental feature, film exposure settings can be determined with the camera on a mobile device. + + + +## Web App + +As a function of the firmware, there is an embedded web application that is hosted on the INTVAL3. When connected to a wifi network (via the mobile app) users are able to control the intervalometer from a browser. Users are also able to trigger functions and change settings on the intervalometer firmware from the command line by using cURL or wget, so actions can be scripted and automated from an external machine. + + + +## Hardware + +All of the non-electronic hardware is generated from OpenSCAD scripts and built into either STL files for 3D printing or DXF files for laser cutting or CNCing. + +Electronics designs are available in the form of a Fritzing file, a wiring diagram and a mask image that can be used to fabricate a board from a blank PCB. One of the easiest ways to + ## PARTS diff --git a/app/Readme.md b/app/Readme.md new file mode 100644 index 0000000..99b3db2 --- /dev/null +++ b/app/Readme.md @@ -0,0 +1,30 @@ +# INTVAL3 + +## Mobile App + +The INTVAL3 mobile app is built using the Cordova framework for cross-platform deployment to iOS and Android. + +## Requirements + +* node.js +* npm +* Cordova +* XCode (for iOS) and/or +* Android Studio (for Android) + + +## Installation + +All of the required plugins can be installed directly by executing the `install.sh` script on a system which supports bash. This script will use the `cordova` application to install the Cordova plugins. Cordova also supports the npm package.json format, so plugins may be alternately installed simply by running a `npm install` command from within the `app` directory. + +## Building + +Once all dependencies and plugins are installed, you can build the INTVAL3 app by running + +```cordova build ios``` + +or + +```cordova build android``` + +This generates the application source code in the `platforms` directory, under either the `ios` or `android` directory depending on your build target. The app can be built and run on your device by going to the project file and opening it in your IDE, either XCode or Android Studio. Alternately it can be run on your device using the `cordova run ios` or `cordova run android` commands. \ No newline at end of file From eae8e50cf8a91c75e5f09ef1f1895a7aa60db385 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 7 Feb 2018 18:23:07 -0500 Subject: [PATCH 379/414] Use INTVAL3 not INTVAL in usage descriptions. Working on submitting this to both app stores. --- app/config.xml | 2 +- app/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/config.xml b/app/config.xml index 000b3b6..4e12245 100644 --- a/app/config.xml +++ b/app/config.xml @@ -41,7 +41,7 @@ - + diff --git a/app/install.sh b/app/install.sh index a25ed2e..20f0932 100644 --- a/app/install.sh +++ b/app/install.sh @@ -5,6 +5,6 @@ cordova platform add ios cordova platform add android cordova plugin add cordova-plugin-device cordova plugin add cordova-plugin-dialogs -cordova plugin add cordova-plugin-ble-central --variable BLUETOOTH_USAGE_DESCRIPTION="INTVAL intervalometer controls" +cordova plugin add cordova-plugin-ble-central --variable BLUETOOTH_USAGE_DESCRIPTION="INTVAL3 intervalometer controls" cordova plugin add cordova-plugin-statusbar cordova plugin add cordova-plugin-camera-with-exif \ No newline at end of file From 42580c46b94af260f2fed1bb5686f6061622dd71 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 7 Feb 2018 18:38:19 -0500 Subject: [PATCH 380/414] Sequence feature for bluetooth control does NOT need to be split (yet) into start and stop. intval.mobile.js was using the incorrect callback on the sequence characteristic and should be tested before changes are made. --- app/www/static/js/intval.mobile.js | 10 +++++++--- index.js | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index ef002e1..b7f33e5 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -279,16 +279,19 @@ mobile.sequence = function () { const opts = { type : 'sequence' }; + const elem = document.getElementById('seq'); if (!mobile.ble.connected) { - return alert('Not connected to an INTVAL device.'); + return alert('Not connected to an INTVAL3 device.'); } ble.write(mobile.ble.device.id, mobile.ble.SERVICE_ID, mobile.ble.CHAR_ID, stringToBytes(JSON.stringify(opts)), //check length? - mobile.frameSuccess, + mobile.sequenceSuccess, mobile.ble.onError); - document.getElementById('seq').classList.add('focus'); + if (!elem.classList.contains('focus')) { + elem.classList.add('focus'); + } mobile.ble.active = true; }; @@ -306,6 +309,7 @@ mobile.sequenceSuccess = function () { }, 20); }; + //retreive object with list of available Wifi APs, //and state of current connection, if available mobile.getWifi = function () { diff --git a/index.js b/index.js index 9abde59..2ac5532 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,7 @@ function createServer () { app.post('/frame', rFrame) app.get( '/sequence', rSequence) app.post('/sequence', rSequence) + app.post('/reset', rReset) app.get( '/status', rStatus) app.listen(PORT, () => { @@ -54,7 +55,6 @@ function createBLE () { ble.on('delay', bDelay) ble.on('counter', bCounter) ble.on('sequence', bSequence) - ble.on('stop', bSequenceStop) ble.on('reset', bReset) } From a37c58d791d5c6177375f310438ca18db4a4e0c2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Wed, 7 Feb 2018 19:07:38 -0500 Subject: [PATCH 381/414] Add a reset feature to mobile app which allows the device to be reset to the default settings (direction forward, exposure 630 and delay 0). --- app/www/index.html | 6 ++++++ app/www/static/css/index.css | 4 ++++ app/www/static/js/intval.mobile.js | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/app/www/index.html b/app/www/index.html index 35b6af6..7fc6053 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -80,6 +80,12 @@ +
    +
    + + + +
    diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index a43fb4d..87b5c4a 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -113,12 +113,15 @@ var setDelayScale = function () { var setDirLabel = function (dir) { const bwdLabel = document.getElementById('bwdLabel'); const fwdLabel = document.getElementById('fwdLabel'); + const but = document.getElementById('frame'); if (dir) { - bwdLabel.classList.remove('selected') - fwdLabel.classList.add('selected') + bwdLabel.classList.remove('selected'); + fwdLabel.classList.add('selected'); + frame.innerHTML = '+1 FRAME'; } else { - fwdLabel.classList.remove('selected') - bwdLabel.classList.add('selected') + fwdLabel.classList.remove('selected'); + bwdLabel.classList.add('selected'); + frame.innerHTML = '-1 FRAME'; } }; var incCounter = function (val) { @@ -162,7 +165,7 @@ var setState = function (res) { exposure = shutter(STATE.exposure); exposureScale = scaleAuto(STATE.exposure); - document.getElementById('str').value = exposure.str; + document.getElementById('str').innerHTML = exposure.str; document.getElementById('scale').value = exposureScale; setExposureScale(); diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 371c523..c9163a2 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -89,7 +89,7 @@ web.setExposureSuccess = function (res) { } STATE.exposure = res.exposure; exposure = shutter(STATE.exposure); - document.getElementById('str').value = exposure.str; + document.getElementById('str').innerHTML = exposure.str; console.log(`setExposure to ${res.exposure}`); }; web.setDelay = function () { From f899c2e120654e51e7fd19a675c6c91badee9dba Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 20:08:15 -0500 Subject: [PATCH 304/414] Add alpha icons to build --- app/config.xml | 8 ++++++++ app/res/icon/android/hdpi.png | Bin 0 -> 6342 bytes app/res/icon/android/mdpi.png | Bin 0 -> 3128 bytes app/res/icon/android/xhdpi.png | Bin 0 -> 8667 bytes app/res/icon/android/xxhdpi.png | Bin 0 -> 17345 bytes app/res/icon/android/xxxhdpi.png | Bin 0 -> 26986 bytes app/res/icon/ios/icon-60.png | Bin 0 -> 5157 bytes app/res/icon/ios/icon-60@2x.png | Bin 0 -> 12656 bytes app/res/icon/ios/icon-60@3x.png | Bin 0 -> 23504 bytes 9 files changed, 8 insertions(+) create mode 100644 app/res/icon/android/hdpi.png create mode 100644 app/res/icon/android/mdpi.png create mode 100644 app/res/icon/android/xhdpi.png create mode 100644 app/res/icon/android/xxhdpi.png create mode 100644 app/res/icon/android/xxxhdpi.png create mode 100644 app/res/icon/ios/icon-60.png create mode 100644 app/res/icon/ios/icon-60@2x.png create mode 100644 app/res/icon/ios/icon-60@3x.png diff --git a/app/config.xml b/app/config.xml index 18ccb9e..3192589 100644 --- a/app/config.xml +++ b/app/config.xml @@ -17,10 +17,18 @@ + + + + + + + + diff --git a/app/res/icon/android/hdpi.png b/app/res/icon/android/hdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..35368193e40dfa6438c353c5aecbca24b5dcb4a4 GIT binary patch literal 6342 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 IM6N<$g1?d$F8}}l literal 0 HcmV?d00001 diff --git a/app/res/icon/android/mdpi.png b/app/res/icon/android/mdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..58072eec19512c24c6485558ef948b80254ab6a4 GIT binary patch literal 3128 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 FV1jSncw_(o literal 0 HcmV?d00001 diff --git a/app/res/icon/android/xxxhdpi.png b/app/res/icon/android/xxxhdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..5c416a4bf9f684aedd159d0a23d78f277b0c1d4d GIT binary patch literal 26986 zcmXt<18`(*(}pLuxv{me?QCq@_Qtktb7R|fvaz#qvKwn+=b!hl`f5~lYHDWAbNcD- z`?`CgloTY9;PK!A005G-l(-7$b>P1TEHvm@Wy=l*^a5=nD=7{*{_h5KMNGud@fygypE+s!O;OFXDB6SyLtTp;k&10 zFT;oH<&Uhs9}ix4JSDT6%h%ZjFHd6y8@3v|X$~PlD&RCjneIB31qh{SjXq zZ=dKY=xqMdarp18Rlw!`>aYB+0QRRHuO79p$*Vj+#ku3Eah9)q=KQ0qGe`Bg>@#bH zSM9BA=>`|oIXGRhzeL`%NZQ1&>cW5^02u%>x#TWqR)~}g_4E@lPe}D)=jvF-bqD73 zT|r5|?#}RukDk_59Pg2d>fN*HxAcz+xV~^^f{*%y()tNa|0N=Ev>QiWLuyPmzaH%SoI)4hZW|q-fyafa$l#ul>tA0L43zz+~jZuZR4SsMg1Bbiy zN=;M7$6lM;vtw-Mw?Dm-7g}qk)i^QF@{-uex}Hb=lth37>>M8E84^yIZzm$Zc7BbC zB;;&QJPM9=h z$Zou1#cXb)1BLe^kA*a;!N0r+5RLdh?0eAN8^pT}EqKnQ+5Lk*;ySQS7}B)m%w2Iv z9_|7=cr8qERWGlLUit?diF_6}F_v`6g-b%ESg|+kxE+T8$7LtB=Z!4|?K`E-{1#Ud zgor!v(d`ie+gW2T80T#;noC{%7B1%ygu=$|`MmZ;3MEz|BXvkNQ&FARki^{9YeOBT zJH+YU<>!skXW79gx97Ief}L3jSGf$4f|n@*lUXa6=l!xyL-(|?)8O|8KUp=#1v4_a zC+u85J{-u0W1{lQ+{jE+)u={w%;@aqMQWZR4M-Z4;CoBHNb-C$O@CB#ZCaa2YD ztD#YDK10$+a*5n)RGF&yjbTV8^oT=+T}t6yv~U#REEehofj0A`v(t2%!})%BLMabV zGvMeR*A3;xiAE~j(D#_>CqG4mvGd!x&$9d|aDpw0>d>tQ9Jl#|m0t7E$T@L!Nv2tc z#w$yKv`bdJYy%S?#1)lK(5sK}U4(L_q3Kw6^OM!1GUMV9vGbF4l9^PwH zIiB5_FSHP?MVGWREJja3g)ZAYrT0<^+^Up24|fU)fw@0d1SsAFO`>W!$VKW~{pj3* zt13eE9x1Fr9UBvj@t8jIp{+Q=5#np$?Iq_heDIfRe1-%04%7K0U6_QBHREO>!ukys z>CmOKCL_3ED)xKwzN21Env2u~=nzW{Ml-NT(U^7&CQ8Ynnij z0xFJDAu{Q~FN9cPMoec5U=$7CF4Pp+E3 z7NexdRFUYR1j6F`BUw^0SuuBc=OUzK%g}pxJds|(p0{u!)HP9Uf_-LGgd@S+DM%QZ znM-P0M;3db8<3a^RLoYeSK34)aVld6UI_G%6M+%l5YHsQ!i75=BN|7nFzy$V+R(qN zdST#D6G&jcTMcrbtoIL~A!D_ct?i&^2i(wfO50{6xM(39q0aZNS6~c>5^3S0FoAH` z36U@CsnG)xZm&b=@EIa*&$U^5G>={+8{c&00{R2g$jHC2K#1+iOW-c59=xRONd6F+ znQ63s1+=1tQvqtv`ai&XvGfGR;r$Roo#~oV%uF4E=h2FAG#PR<=HV*bh3Gv{)Nh8u znECQq*JxlaPHN=-a`13_%}Srp>E9NO2f;J9uq!&f%b($9JR=i&DSI#gtzYpJwfu;7 z2xA!&r@f&*=|V^F0)6gvE&*>4K`b7G!XHMRQQ00e@J(YhqcimZjbUZomqT$ zBuS%@Ally|pc#@}p^!LNs;*Jda!`Yz{?P1TtBENQaK0(aGD?adw8Iv=58ESI7Lkgq zR_wjCmV?#=dO;E#c8sUyTd$ID!*jAGd*K^Gx!6LHEry|>US{!NPP@qkaZcVqGsG1hm?$%53M zTGsBxOde4!PnxR&+p6Y}tHk-*$j=+r$m&z%i9i0D8SZU#U_ zT27an0|t4kgE$Z+4KLW$%#I8II!Bq)IfaVmzJ2QniL?SFG9v1v%)$mmcG3!p9amry zp|d~Hdp%mR8+$up$h)z%Rq4~O%uolH`>5d1;|?)O zIhwn`#EN&x9=KG7XtUtwYvEu`H``Ua3#CXF5bD4VMPP|T+Xxr6@I?}Rvw@f>@rJ`9 zbvbSPNc0MY6GlW2F*xGul1Ek0I$+m#oB2!Y8=rpO!Lt&T2rgO*tFDBg861Z~jV>mi z{ZxLk=USf?dJN9K0udBZQeR-=p8uCFbY4Ta&;oYO?b#XisBBQQ1KR&_BHr8c92=OM zVCn0P*KrTpRd!sYT6nmr*+lCDP1%zaEGzV}mW;?mDVeNu^F}=IbdXK4f|gEfurG8` z#{y`(niQ<-QNJO=gCU9HBKB+WQ=4VbIdfo|!#aE#AB*o-@bF|gYj2}bLQ`H4aZ3x1h7RrUcmRIiKGS=M2dY#!r&&7pqPcRdg7=mp6vO8EN$}7_X8Y}HJr0Kcbu`M9@uYQwquq)?<^ZRTdqm-c zCQNl@sGKNyW^;n|t7nF*W9ZB<{Y7fxam)YzaWRFdTiAE<(%gNq5l9?&*j zPype+8KVrLA|}6JjVz+?m$kSeNL74UQ}ZMDgkt1E-Jrr~%O2{nKt@s(gerH8vPwvY zt95IM8kqq-g`_pcrk#tiwP2?(f*?k`;gE3fGp2R;j>w?abz$@Pl%}Qi4p^@xCR)O# z4FG>=@W{}M$Yd{3hYOB917?LD38Gm^C}6*BXd#U?bqfxz)s#rZn}!O(5m1VfPUI@Y zIwN@A7DCn~yYZQrs6_7WOskUR(*1t1RCfTfQ_3le+TPtX!_`xc5!3|Iq_RP;XH&A~ zxnXtuwaP%YkJOEfK_@d?B-dTMJK+~4i`6slD~7R9==jVvK5OpoR1-WT<>_EUK3PT;MGH=5DIPUW+cxBre6uH&kcOPU z*%Hrrf#U8vN9_R||9MbuJ#R6s{39?=JC~bm+`Zen^J9 zS07eesRLD0rC|MuCbUXKa4I4!c_m^8HUA(L$iDI6n z2nkK$A!h=oq=}m;N7rO8h+H(A2ear7ds`NH2s70a^ZoNGKy_Ue%(9M>#R_}Yxce0> zc|LIQErMQQkHG87y+o?rqcoDmg#g1kS1n`UJjabxvPPan;>nl|cC_{KG)Jlki8Tf( zlzxyiMyxlP(R>CAT4Y7~`%dyYLDat_vs(tv1nX64Mhmk^Nw$C;@JMD?3e_1bcx>NP z!f&BPBT6cNQy@Q~hoqIi$eec#B5&gUqT9p<&#>m99yq`nGr`aFS%kO@nUu6Pil0Zw zPDusQ@cZnjVxJ+Ypa*9e&;5z*rtE$(nl||5I-#L<>~xfxs#53^;A2xW;JUOWaGCrkh6{@TIdZ{?J)(gc+wdH9PzZMW*4RKD zaQ9mtx>oVF%Ea!c8~f=*i#?;AKJMO?RNdD6C^0U(@LOs{{x7pDAHQjBY`wSa2eR++kjYj~qmZNxn4(*^ zpY06?;K_d!(T2doc=y@;YIw{|+T=3EZBItXkXYdGz#`}&kguBR4#XozDeFVcB!0^Q z{S0)|NWbUTld~iwNo>4llA+MlWR#JKkCLij#v(u)5{O|T5R6>S3uxBM8_g5Uq-Kyo z*SSQ)Q3X^{1J(IU0Dm$7(c)=PW)!NpZepx5PHlKQqG^!z3Pu{B;g$)t)+owvmWc%7-iTrsk@yzpCEF*cEQX|H4%)Z* zk?P@heDV7;6JDtm+K|NIVB7EX3%DEU%7Y z`oR&H`I+)L7h9O9;ZMh-l|Oa3Ma4BcY0Z57?v=t%!jKS@X-VK%t_3^iSYorY+{giA zT6qfkSu0#IC3yYur7ewPK`chS=LhG0A|v2dP&rL4a1)Em+Hzni_f0&7P7GBssdEI) z(ZYXZN!21=e8qpXOV7wB%~Ukey=B%TqWlZ7p!*#qw@u9uHI3Sc8df{9RPSdbyF5+y zIvMHVEArB{j`}7=vF=qW5R}5~>>8tVBBp4Dk7GtHQ1b2a*@=dKc$90h8gmVDAp&08 z{h&Dl&!!mbdyr23))9qBppsf^K~axb7fs^dU~#8tJ81W&EVcUZEm`7lYwj@&6a^E) zz8;jGL%2;X0~DdxJ_860W3X4B5*@_G0Wunn;r*{a_HyxFwE5(JrN@S#qDNz<(v22N zAxmS@Y?ab1Eelqy-6N=ND7o%}Vg<9vAt=?a! zl|2*B-QPfMibCo}UPIiPqnC?b8(nP?nSsHSYLe($pOLqyJy!a`UncAh#au(MCL(U+ z6GY?DG?Noy$299vw|j#{_8K>ga_J76&ke!uWSxc!=_9>s<0FE6e5j|D%&fJAR_Fv8C>< zOLWnI8)xvDx}{)}h=}GN!8Y9)7Y48@$0(dkOq0yshJ10c1GPwPs$+3jbP^CYP`S4R zx4pzQi=E#msPiM)C4l(|SNoH>O(9|qDL3-uW&nZ~Sj|}UiVhOqyLeubnwL=j77BJi zhJhD&R7(1jqX;b-GG&5r5H-4m8cT(DNW+l}g)UPcBbc+xk_Bzj5gL2g$S=`&oF5b@ zXkCWr`fB4HP$tj}$=gL?s?HYDp$h}w?p7zTNUH!--Nr6y3 zJgiXxj11xgUPp@@PJm@HO&V^_Sv<|8%#|T@fZ7Z5yDBZVNN9K#+}RrXvaD=&gA5-D zys`NaC$$28x1KjnjN$ibpC3Dr2H0%E2ja%4J@42G$r$;+k7^Xrf-xsyX?Xl4{>E{y zRcgkR%!eXp{lK#ult#=&UBD?|I=N@3@|FVYp;2w=HsA96>0So;n0fChG@vK4t4fMb_baY26J1FI_ zg+Qw3fH&HlGDz*QmuxVRS|$D0-6xDqn3a&~*Er>nKEtIU6?kWm3O+>(FOrN6QR#dP zt$Iq5ClJP&t3(rqXz@U;+cc_3Hu-mDN1wJ6k}@lypcp0I;ca5akDrao71GekUx~tZ zsMPRV-%P0U$d&b(VVI@TU~T_4hTXALB+%%w@XjkjX;9*^+)bFZhE+s+XGEj@8F&aE zIzZonq6~$Y4ZE3l#I?F?qEKO#I)Pnj#R%2a4nbt?icF_z+}jWbSp4 z)v>5|)+kx*UJEIX(8Iu(q*bq6DDLeHMW21TNj-a4b|PP+-1yla z6K<6&LKiC@$*c|={B6HZQaP?3&vXX77d)sw4g2QEtpv^sIgT}|01|!)Ue^n;)MZ7P zW#l+pKzSaa<}rKr}$^%@R~r>d7wp-KL%$C!XmPhs)TH zr6xZ_Ra^eJdwC66nd>supsZ3+!DcU=Jy3_xuT6M0#)Q(IYc>#L7E%q6dC!H$RCx$p z@vdy9nVj|GIFkNoU1h5cnD51Eb)S#r0^F~}biS2-@~H%WI6W{|!VM(ymK)Itp0 z9njB)b%kK3GdNPHuNo9BFxdO$EPiq9E^%0I+m>GW@Cm8lm$oskP=QAm3wLjiCBqPp z)V6=3acwz~uhwXXd@8n{)RX2_ZTeo^(UNN32PB(5 zn`&hS*H-sJ7o5eKAl`hrDzm9N!e*#sF32fxO?Mg&?9Sp5t+}#hhehl1{MAO;%8Gi` z>tq||l~bjUx((?SpDrQU2gNgqKb`xt1=8wQ%WG9s-ERVwT}B+FuU*?haoX&=)?dCh z|5TrW0q?j%AE%03R>n9IL7$)gE(I%(?H2D_{O05Et$ay83+|rMvX|Pk#CowW1lH$9IgA8jO8U6` z+x5j(uD7*Lubpkj`QIwb^XYn_J3f69Bz{m+y$q5TsD4_gkZ0VNkw6G@#WBWU>>~%(iYLcx9BkR>_KgsX6U<*#`9f<%3DlZ$vMdEXT^{io0(y@@A~sObYF6A z`QFWFb-OeCxpa3oy?;X4E5T44L2%DZ(5RNcc5B|xDQYzBF3Kt7Az6N=Y!Yt9FU@i! zDXNHbIe;npGyUmxKoyP9$4hZJSj`o*E`M@FD8S_pe|NHG%=@scI+_m-Bt0#rrfxFK zrJ8U?*vxI{#E_GBVk=s)u%hZP$gLa**mmY)?AWWuB_u43aXwqF^gP$yjn?RIq4& zhW=fuszTDPw>1{G5ulhr$uMXFFs{kK|KJY)hL-BW$SX)CTVvn=OwyKkl#KJ79bN2gBA}iFC>h zFd=||?}C~5@X)?#qsvsKuurI2I_rB83bw`P#&hFvyw>ss)k=9gF^WsTi`efi)m2qb ziyg_inbWP_S!kzjxn$PviCh_n<`cN!yQejuKWj9h#DzBdST=ikX{dX-OnD*QS6$!f zTmDdA2sn(GL?sm{!imw#fphFMXN`%h5GW6*z0hkyO4Tb7j`~00{C$=jXH2pM zd^nLo7s#%4W!0*EoWUfOB?zBjSryU*0y_q8Gbjjb>ocQ&FSyvuW>+tX)LMlT}q z$B878qkvDqh+9dyF1X=xZ}8>cSKN~n{m#~6K|X$dJ=lP>QTH# zdhQUPYfL!B4R+hsh2x0wV5h8yuw#hS`BxywAa5P#tXUeqz^kozb;yUcT~#hlk%demy#7 za{75^5LhzM8Q$Or?)|H|fG*-BNi@6bluik61l647-KSyVPt=f*kZ+!B(0fN+P{3^; zZzzhJ`o3S0jQqOQ5>~x%p*uRt%-ALJ=2?ybkQ$f;-@#%9098*ldY$Oh{_o<=&*k!) zxR!qVc0E^a^`j>zG}d=hoy*Fd@$JZ1h7Q7JS~j z@kPMN56*Gy>nsz2?DN+Xx{0g3Q*%t@)I^_x8-7nB!!;5uNFl^ zIYq?`U6Hw%yxhfTev&-|;(h>EX2F;r?`JUdeBvHWyZcP=mHZkBJ(_fp$3FD%Eg z>szlkC%&u({Iy^uLcENn{l57=8`4{_cL7sx5JMQ`Qv8fku}hVIi~j&zyHly_Ki1L) z_lG#F7IF|fVKwx~zV>Epv77D2N8SI!be5eU_)0U&b49y%!6pFbNb<5}SG z_L7UkDi-pq>m>v$%u0jnrt7@d8-XnU6$R!s!TnGSW|t$m)I8H^>2j^jX$~LA8Zr43 z9VUUQL_J;2ewI*;S)hd%obtS_rzb9s?*lj@A|j2473pe?fjw2G`X4Y7a7$48gn&A?bQggI(;arjG{~zR6A5#NM!)ZBq;ljjL)Z>=0o6WCF18Is zS%Y>NPQzKJ!`cNx&4;@lhLQwckQOu9qgWS1ttzo1%`@+*8on~PC)eZi^Qa{;ybStX zm?#4Gs2coj;N;}wxVX5&Uju+w9ls(JVGQo0Wm@0X)NDL;erD?f@m#N?EShMAAPG=A z$1+Z^M7sEo-6PbsZ@Ni?@)0ZC;N+A2$BDJ9DH`N^Nd7dBawAckJ&BzU!g)qoaf}Ue z)ne-IW93i0(UW*6$m|3ItPx??2Td_+qXt~LDt*M=O}%cZ=OeEEsBtytDgU?22*RUl zCE_1Jo7>Cru7;(>MC58v>S@xnlr67s4QHq2zzOmMBjCYk$6-95)11K5eF=|Zfgs`C z(ai8w_jNo&+d6nN=O$vQRC`p8iIt6@k4%?BNu71Y8ANg&7=YPy3^CX9VEiU)&NcW@ zy;@_xWl3RJnmTV;&7i1qdGXypaR^;^rW~RC{+VkUep21VjsLidebBLKs&)Pzc1cm# zURZzL6h5!2rk^pt3Yl}JUQOLBuZm+kP6`J&@)}LYwx)?wl?k4C7Nl-2FG`cPWR3~6 z7zMtbFVeJg+b;}@$r26Kw(sbmtTT3B1p7UD$(J{TrXHMvA^6C9;8Z%&lVZXB`t{56 zq$tAav^3*jKZ4l*$0&1x&?onQYT&r@YT8=3~o9{xrPKf?*orFqI>{qZFs(Wj% zRnz|7rhe=Y7UdtTwyW~z=5vxi#?W7+?ka2duPgaXpQh#5AsWx#zD zaPhN|5z{}#sLc?3afMQEotL2n{vv*d6?z^-xq0$s&}o5h@y5s0b8-A zn}KdM4>KX1hGgRewSF)JP5}R>?p-SaqXPo&2Z4UHkaCdqe5q@u=YbZmXWsava z=Wb>YMNp`RgjsbZWyvD|UkUTo8;iu9~vH2aXf_Oh4~92E1)H0GHz!>Ky(=!Jo>|6d{<44ppgF zJH5k=A5ML*XWz>C%BhDT)(a+2;74Ek+IvOb-Z)w4m~L@II%`_Npi&qZ3PU)z-SxTd z?f7`y6ax^YZY)>Hh&B}gCNIvPoHvpK?Okx0A3;>sg=(BaFDHtsyDMFU)h|}0L_@E? zE}k_vk@3>Fkl+cD=qg5^z?yNuv8Ik#wSA~F*AhK69$nu+?Y%yfYev94XCgx@9G zEcdSvar0z@p}(iLK@4U2lF3TQbq{3C#i0wnD%oycEU7W`)$~heD@)(`?=O_cW812> zr{P~>_;jVj`0)3aERcpC$Y{mSno44Dn8wZ+XORHJuk#UzYLsL}QBYdYDoCgE^CX`i zyI+Kw`hHmXmur4c+YwLwUmtFjCGA}k+AI=>G!jmKtjkw|j=S8>Z-|9n5_qqSU9p^h z-X35_$y-Rbq2wkW1@(CmNqmO7LL&8$i9co6<^5fTplm7lchih(tf%-wOV3%q13TnK z%7apnhz52!kvHw&(C|m2I_x)Ncg^Q^E{_W+PsOeuu>U&1rg7L#`HE%x#0q#nBnIkv z)oM3GgW5I%B0two(O%T%%pI`gAF`HKR!hN8w7}CJjNz1Ur0j*}`5#EP)dsCxeNVmDBI5H7?JWHKu^<@$n%_qq2cEhQA`Q+ff?y1l0I+lpR-e2qgi-XE2QnBpL-;%lR* z)h@Y($n!wDe>$`jdeioc%3`y0UqDLa2eavDmMM!u_pX9IdcT_+Uo4dc>iOq^b`R+L z@0g9PM3Gci70WQ(Wh!d2qkuq`i-uujH&ow`hqc2QM}M{-KYrjTGh5qqKR|Y{TSCa< zjcDNwRxihTf$c5)`we_)o*gp0?qPeg1LXj%9#0l*CIda)$KJ<32;&4Uh#*R!(M^Tq ze+@M-PcbMp8!<(Y{c4qGm6H8fw3e9O`WR#>W&7G;Fi(K$xbvR(^Z9ULZ5M~4dlYRov*c}D=o9$L3z977 z4$$h~3tbS0c;A_&)buV^Yob;_Rq$pQW&ACHyZHEEuGjye+Hij_1Z7NFl5mAq=k|v3 zXAh%CKwpefsa-x9R34{Y>N-SmgZCFuZ_@dpS$6n|d)z?UD} z+bgtPj41qd-94|_ulw>>M$iAn+_SGoquwB{-tW?uMkj2zZ?s5U0Zg0s%uUx(puKOk zG{d0k-MVpV2|X=S#Dx-8AVjwod`;hjaOdO7!*nznYyVr@j?H)91B?P@A-=;{HmB$P z2-CY6E|ly3x2x{Pug|x`N1?Ay+L4U0+lGAbnh*7Ue#RCbYWXMd z$z;5s>R$GGJ-M(Ve=WO#gk_kdY#{~g`pdG6J zrr{jdkkTAFWXmDfO=rm3f_Iwa~{Q72Z17HX68Wm?pU*q zt1jyddqhwY8JtCM+ICypHzjV~xVQ$DW$yDDCL>TL>Z^v+PY@I$+57m84%->T4tM?8 z$DGzEs9xitkX*J&AZr_fBYQKuKj7$phmY|dpODZEiG0p%4YY|gy8P4H@p@Jb^z{<} zX||4oXo*HD@T_9smE(``AHd{{9`Pb3%|H1TBkf%2BkCbVSEbOtN3nBzZJ_Jk_Xy$z z5#Q^3(3!mVV{*A0lsGFke9a}*JPZ5`;y}q25!8uP?)uDd?2c1r=oOQw!fgelWKF48 zzsR**$CFREx6tD;Q389C4c_3d;KI&}uux@NJ$4q(Z1fL4LP)&;~FQqm731o0`c4Ko4tKw(O3!Y*vU^tgXa)hZ?r*s=|b;C zrt)3&Pzcz2TP|}FhSe*lQd!^ep(eR#n)JFEGI`zT{uA)ixjai+13p)s;NMLB1S#RvD+S!AB`M7FLa50NH46nww75In zvDg+o?G+O_7;B7}S>cv_sq&;~lw#iiTV>fYc0Ir`LLugE)vydp(VJ?+5A-ip15HCe zH`Si~6T~BSwHSxR66quM-hE3hR9;9&;ksJ!v|pWj!3fg=erHPsL)opiaO-GJ-#nht zJj51s{$mAfLID;ZS^rUF9K(0{?#NMP3gH@3>t;OO*l>qz6WB;7F7jeo5mRbO%d3$U zPxyVN6N#D!7C!s1F>;u&F2$MN`=^HplFOHO@3YEs`T$C!e?T>SeEeYrzAG~&N@99e z3FK}XzJA?So3zHV|4-3!`JyG#M);bR`h_QW=lyfr7K}SL;atna-3QU%=WSZSJHXf8 znFHkU-=QHZV)2xa5YKdekMC6TMinz=mq6RIm#&~>_F2fl{mx*jC-(i#(0)P3g>cDl zrC4Pl5}S}9bSP7I{A#;UEO9iVDPdS7OAcrmA8)+uu|J<<((h^!<4Me^Z<{j30=R3+fgIKQ*D$_$TI=+@yc-Ul=+=e3yxx=SSb27^H?+t12F3pyQ+g z*3U4yrai;rLuuqFXl>4xjj2}iIQG39y(nBe4e3phf=EnGz6-*0O0$LWLfhs+m)G6R z;bq$}$KBCB-=AKMO1opq~Ryj|d!1q2Wqe7%mr|EDPT{DOLk(jpE_#hlQJ z;iPJek~k8|jv1xP=H>7sZG!v)L(P_nI5+LsaMfAUvlSmd(F29PXI^diU$Ps6*X41c z_#mN}cF@erz)=Q%rZRV~4DprA51WI2>ZE>J#FnkEZ_kp=q`dFVuN85};ueYDO-o%I zIm6B-f-64hOok5dVFoW#nj%#JvePfjWmd2ulaZ_D^%FH#Ez;odU`B@ zsOe1>-!>r&*WzvRD+AXeT_va{MDqmHdV40-iPXBcIYR`zZZRM7F@JBM*2*l{>_>q@ zQ-S^yM=>+d_3@v+lwez*3n42SS%i z$HXEr;59?n?=LerGfgBV2&w6(J#*v$b>Z^yTCZ+lc{4!=d1e4SVv zE$I?mxJd`9_4MPFm$kr&zyXEA{^m(|WvD&yJrLZh4gkSNIF)n_JDE(u*jh6iEU0{g zhx_KfXNRx9Jga46*6zSSW4?v zNsM?GBrY%X(vwWZ^wlvSI|8*}%XU9LJbef@9hw>Z{Ed51@7MxpHpPp1q}T<-rCTr$ z-s!+pJwtkFYw4jMOVeA*6y3H*9ERqV#x27%G8%YiRJs3MAiCPOGF(R zUL;>4pU=860!AV;2Tdxcut=CR#6Vw1@lr=7zBOU)q<>kjX+4il%M%t^H!T?le*chF zm@WOuf~z)MF?VDeLqzRWza~p9KlOQ4REqL#p66tXPa#b>HvXim>sg4$p*XqF*}S9+ zua;hxNzPaw$4uOQrTz85B`37$*$?YoY&&M-UU=SN#WwNQ*C-{jH!xq6e~yU%8|5dY zOeEdlZ3r~L&B?FdwL?wk93&!VKTOR2M$cV}VpFeX#7Y#fn1eG_^8)08MAe`y)Vc5iY}^Bi3J0PAmNR-TQ@AcVoKH(n0UbA= zJM|H4GaYMBUFx((p}0-Dw>Vy}z}=NynXoGLypt^0^rvzO$VJA+f9bjvB=7Fz5;R6N zdXrS?p%V)+PN!bNN-gm35A^}HLj52)%eRccDV;-UI0lhobKE7bq%;L~xer;6H*Nzx zB2u0-#>{S9(YIx_g1wy4z+%kcR-nqc^UTu-cBOo^#pd-Pj0W~$n@ z{pWx|mgc=Cm-g<(X{agiLJ_{2vUOV@lITgp zs{qRE`DZ$W^Z13Q>0U+VQPk4y$K8Tfo6DJ0`F6P2Ot+5(M$h^mlfb+Us7X3s!(_CoPGDUn_RDtFmvtI z(Jf(WSXO$~R9JET3~Pt>&2_UB`5QfNzq2ICn_X#sqh>)v;e@)+zL-pF^y8mkW4is7D^FPe*_icL9L@e#plzCrs4KH zdM5r~Qrn!Lg|(Wk>fJ}#w$rCyUj+W1DVSK|v4+2rs1hyoD014ugep`aSTK9(!i%3E zWin84f*R!NW{*xs#TR$)P`++ZJQ@4yylWZnm4g)%ef0=$+ib2rxc=rB!T{4eV6(!7xw`$M;7J>YN40OtKv|JSAG%` z6C+iC$>H1;j7FVa)ZLs=|K~IaF9N&PdO4+zBC;*S)2y8*QYu`QsY^b7ObrzFRkC{q z7$!cZLc_WKg^Vd3__lySAb|y`+u04T)(f>qfyVC)A z?*F;ScUg0OS1!2tgdUR`Yv8L7-%dI*Cy&4>iuuddz@pIXa@0fv+JX{7!2qERB6+c4 zR+WN-@k~37{?Dbd8O8PWyEMX)&5Y=*qabe-w{6$|?TaO*zrUZD@4Ba1?E8BaU0mtq z^>samD45Z8?^C>tjmXiQJ$)l%z{A7YIfEwIx?%%9uFKVht? zufE@8^uK?*_L}pN6PSRb&Yh1tHlbcjwSB?+WDvQ9fj^_+#Hvq{b0{`)Pxl01S`NmR1!aA?ltv+DT) zAPZIFu^kqyV@*?M|J(MpXf9eJ?INye_vKO6Ie&rSb->q~J+=7o@1OrFwt@5dbky?M z;wcsfZorh0kTLT&Nl8gy5Q;AVWULo-j@#DvVqbfX^WB7;2LyuF(8S4!xvL27#P-1$ zq=CO{)C^#0>IJp;UfTZ$i5+&_mz5=rzdm*s(ZoqcH3h-Lm_-Zgp<_f1y)P*uRKQN= zH%429RtSIOFea(nDy~ebw%;$MVNd>c+S~yL%`{~S__)`857_m4DpJSHEBo11t{$A! z{>ZvWcX`+R^*CSYx(-qDoEs&oNNLI0P#v5F70^&1y-yYM2My=12b|d5mZTzW3iUibyN=5OlO(q2q<(wVV3dAu?&-pzFCVNO%Hc z3<+Mrg+S2jxi6*keT=Iw2;ZTDl>QS0+Iw!(>PJR#3}dyMEmm583l_#Rv=9_Sl5H@_ zV+fNv^)6iZj5$_RSuTq1vA-~r$oW^SS?J?SY+S7CHTLSua!;a_(jS8g~TQN zHb@#laNDE*4x+6C7cw$>1Qx%#5_2YT_CNrJ?P{*a>yBO`Yt_?9pU{{=*{wl4of|lU z0laiHj23uYHw{^6Pes?M#Rg4V2>Cww><)t*wGfb_|0j3OMv8PK1w9oyCCVnwPT`q= zy8u?07_olDi^HnRiF!rGK#k9F3G4?KOI=^>6~75zcWhj&NyN(?uzUB52yg=eq*!0) z@{XdkZIjkNxE>VB~GVq5Yg{&&oR>=U<|Vw#Q%Vho^n6 z{uejDxXbokA`qWE3%%+pD6{q~fAcv+i*jr3+9mdxVBc}kZUZVx635+&NgCVvB64lJ zV)%c(?H;b`I@VsBgT~YT14IoVENuiDHX0ld7D9)Qt<$ya8vh0C!{gfQVBTIS))!uh z?fsVRaOcQ=%@w!Rim%PFpz$#Y|3eU?Y!0J}$ZcTIdvdIf5!w%M(*5po%}|Y~=Qq^YxP&YtPcyR;v8!7X1xA_#8O%5)-*w{%fn%Jn7=wGVp3jH+anE*N zmh)dJ^yeU(B9`KB&dxnmYs#37Bo=^{j;RVdG|_DhpC&5Wn?^CqGMEr0Wb zue(pSGqN~$!zHRLL4|T zrm4Hc7>g2<$-=5qJN%Q5Mrh}?=!!+ktUK-sqCO6s3IYJUWT%(lAOG{7U z7kuO+AL+Ch%2-X1%&ZRdPdTLtI5*pDGwi(cf-rBqR@?i|c^AuQ@08$Kc!}?iK2SzV z6SkPDp?oL1W*A4!>698#TocGcLHAy0xk0wNP4XP~1WcDRIlMpWu85bfVCEd4oeIa= zZ9~8UI!eh6`~@z5;Y_+2u+)J^Kk>xTTk_K|WnTM5L-6vhV=#@i^i%j}fc$r6sLL$_U z$H3X=sR|#M`a*2hr6qSm!6f;D7N#8C9e@cFCSuP$_pHb9w%cyQz4zYhcr74OrWuiATWQ~JnyW(7GF zL4Y1DN@6e+)2PnN8wPqYi7&LfOTiUV=oH{rkj)Z$2gCzh2%2PB21Xs4AOLKF2s%Wm z&>=7urv{KIzL64p?6HUGDZ6OVq7F{Zio6xU$?kBe@n7wC?zrQQ_?M4;%yd@#zx5g% z9K><|%)eHdLil>8E>80)Mz`y`5yzr|SS+}nKj+0MbShp1wM9vWH zk4u7N<6#LxH=xK7^GjQJgG9HHZTl>74M4W~0fI@tW7}gV*e{5AM4AF5Gd)9r&;R`Y%(c zv7xW6w%Q5{7A(LHJM4fNGp1vFypsshj6hH!Rri8v)VN9KvW)ar|WhV-H(lly{q?TWU$mrjS z+Of4|smXK+*C&N1* zYq#4d+ih&L(MEXT3txyAzVL+|g@sC~ETtNXEX%URG4l(FdktXkCi;8K&+ z<(yUZ1F8O;5OuiZvbAPzYKxPc1<(htSYlFQ_cK4RhhDIH8m zCcP1g$ao6Gxo1{UfV@jb+ZY)g!4XFsVI)+pzy5kGUVN7#iNzQKzT0IW`nkXiL<|^> z{L1Lq7{*4&Fg7-Zvf{l6;b zL85Ok5%VuODRI*%opf3^L2$M|7@r&8z&o!(2t$Jqvf4tPz)3)`Yr|v!IzKDmqj8t5 zwd6KWPKxr~p4BKK4qg=(kw>{}ZHbON@<=nlS6_WKZu`@pBl_W+xjLv1jBSaiTp>dG zpTUX)7+xxa-eMZl)I5jszV3J5d+)tC>#VcP^PY6lC+gFXH7OV5q`^VDcv@ktkX_BH zy4jpSBFnWGLqfz8A_>blsJJOR9f)lu5oNZAk#C5nb1kJ>i-kE6gln*XrOM$omLw+2 z7>7iBD^{NNuWok2gbDcI2R~#6_s2i}F|N7xTJ*L093@sv6k*47gbn5{;sv0uC-i%5-!Td>7 zf|f^`MV8?q`2n6ZX(En3`e^e!ciwp?&OP_sK!HfU4_3gsb;ATE$&R3Bnsb%xNk&9i ziTsE+_eIgd*=L`PAO7%%=J`%O`D9F=G2P+Wl1)1Lp*H+_@pT+N44wC$im0+zcwE+J3&Twccpq2NmNMYL0aHe{6i*xh zYG`N(#~ynuCaLy9Pd)V%&OZBWEMC00(WT=8)WQSz-;Ymz@{?V4ye5hE=}&(elc!9MnO1!;5%DsHefxzIxm1Z*6`Sd|b=pz6 z*=|0`)RH9U+bcmXIi3+t4kLb$68S={S2)IJ1;mT)v65&<8?o?zXSbUT?(0! zhKsxZPULg$J!+tmx=!`UqA2jvm%bE_KmK^#ZnZ2+EMB}AD^{$)3+BH-T~pnI`MyN& z0onJ%-Fx+3_Y6smKuY1c^jM`-;PT5a#kJR7Ym}XL*kK2paKZ^_^|d;%DfxVj@*ue( zYsMxC%&f1> z_qYXR*CFgU_|NP2+wTF=``-6Hv6HqT58e!DvmWfEn=h(=7AK*;CS9D(BPMlEeSLlS z!yo>LpZ@eG;RJf)o8E+f*m(ik?J~1~avL$xNm$>#m@#A5R`<}~-;bMqdlSC_{7M0Q7ifJ4i47W9~~Vd-^a$R z*W9EY2U)xp{hXOE>52}=Yp3@ec9?W7P)(v9ICg5V_w}+M08&vWf$YXeX7kiHr%tk} zP`kNi^8l+?t;Xfwxg1Z{twB*Qv;z)25Zlb1+taG`dWH?#3}h1ACa5Ou?z`{8C6`=c zPJ95EGG!``JmLs!I&;(bee5t|W9AQ`9t)javl(~(4D>zj+O>oM8KdTqOY61H{2Bbo z4r}Msx=PM0CpOo5AYsEcNHStv$=o2@h<;ZC2?GNI z_{A@Nftzl+sUs|w0=RWH-uIJlqfDIWGq;69qH5kbG!97lLyP)ieRcBUn3(%c{ z)%RY0*=1Pz@WWKEa^Ado*mvLmg~6dAw2BrrhuV#y;}Kk)xF|~zjE;`ts_%Wz=-=yW z&6+g}M;&!kS6?S?p5RUr7d4)SiN`F;b+8+lSOVgX*6z;+SrEG@+1%#cN@?yZ6LE-u(OOTo5m{W<2qFTd^MQt~+tfeCf}LyGF(}Vt@vFJ}A8& zwgz%8cp|_+GC+(QKfg&k8(_l%x#oLPJaslSD0Hrpc}Ras_3#7j=j^5|iUMobuEno@ z{cGI%$3L!@yFWNMh?z5IV(YE9#ul@;z>H01V0d^KDWxv|N3d%3(^#{54gU7GzhT+3 z<#^MxtKU{V$%yZ z1hKtPvc#d{5Zn>>(gnr{3#l;kn;qj`Ku8aykoxE~FreXXZ&h+g4mR8+3kPo;$NKY! z9(t%)SZhO&7#2|Y-K;+6ZhAvu&}W9ycnhT$O+tTvA6BnkjR*ewzj5z9_u%fk@2(Y( z{~oXY{(j7#KOZl6!3(hMw%cOL)Ty=Je-=ETv4ppf*3GDyor!}GJ%I@_R&{7- z`u)KL>4^|#7P`N$9+smyO!DwLh-+&sz#)emA|VP#uLYQ~2n^+62urU76@%XbtQbw3 zAJk+r9d3~&pjEWc-`|gwD_3I4k|lWLkw@^v6Hnl&r=G&<)vK{~?OIwK)8F5Zp`jrR z4-aGVlqr}#Z5n3Jo{g=x+6psfOvl*RIND{KxGB+R7qUrH7HzBEPYx=!5E5cA8GQR* z8zm18m>y2@VJeV?>@)Z&087V#r-O~W9i&~Pc%t`Uc>53N*yyEh}MRItu+CRHiaR97hEMog8OsyZ~)XH}U*yIbsn zah_;0=$F~Fwtyp+LaIWdYLc|7aknI^QZ2R>QLbRFF7!|(fN<|u#Z9UPr>PL5pHr1r zXbQbdiAhFrS*+w=jSl%j`+D&TT$viV_v+fE)Z^}YxYS%H-Tb2F6s2zC3$+#?RpC{$ z#wDpXsh%|E=<88>!%)0^dsNe;Gti}(erVK%LHIu3_`%5+TYk8!TcC~O0#BZaZxJO6 zLq3CLuu3ev_ANZEDgtXio2efV&j#~!V#|VLbneLgmrRFHCQ9XdKZIKetXD5<4_~7sng=Cx!A_)^4fh*TtQHMlcj=beHBiY(Pe=AO7j|w^tlN;N zybu?orWH$AWM*TMesVnvbihS-wc2>6zpt$c@e(VMG7mTcw2?~;#z<|}!qQ7^d7l?3 zqK))`Q1PYz>?ylmvwW@GMbeu>tg=zDl~ORWf}yrpxDIEO(~L^T_8=155P~$Eh~3*`b8`tBf^_3VtlbFX zu9{^uCryf-h=LnxH=}W@#8!ftv1SJ-qL@}V0E7tsinSC)=+WeTxX?zkJCun@B@%I? zFeoWM&`fC67dg9XGVeY?m`E&96kboEJTe73+Ew9|M(zajrJS#E#D2uc6bZS)lm^bRr+!eN%Qj@UWt~2L6|t&>I;JY@ zdP}6bC!SK~8^C)3-Dz&9pPNYyD&`Phyg-HJKqPTDNxuO{k zCs~UAerO2JROrDMeDqmJ*`^C#A-sWM)kwo1OtB{~LIV>mSZkjtjw2*Cu5Zd{#hYoX zSRJkzU0DKOG^nCy(eS?XEN9RX2Hwr$X@6B@@($5G81L5JFs=CIolEORsbm zeMwOR)UuBPK^f&Xz_j*RLAu2=n8{`x3)$(GTyt{$xfJ?*=|xhv)r*!bR-}@6qt`qO z-_*uCK#)SrqEhtK7JgvRqWu^%(YV#e8|wKAV=CB5=3z6^&_Op;K=5@_X5b`KyUPa5 z*WePZ$$C{LQ`k@IUw!cHU;^zvlx*XZl_ZJynna^N^yZal>2#ne8P#Kxe=! z@kU0C0VF=j#d|3>oDi=x9`t>B-9v;$ZS0YpR0}Z$h+v0B3@y4=Nbupr;nk$Mg6r3d zZcGr=2;e(4g1 zLR)ZsbDfrww+?#zl7w6WN8mD|U@oAy0Xc~cOqWN5LjX>~Tv%Z~@*W1y=foCC*`8(a z&t;L!0vQzsmzz-#SaQt=TyO&+fgn+YBib@T(G4N~pjRg5rvOkA4g1n{PV9|PaVhsq z(u*`}W!;BiiOS6Sb3%b_7xii1UWj`_zsFtQxhLX+`xt|G!7HH_pEnW9#a28< zvbhA%b;8Hky0(Q48kBg2OMZw;WlG3W#)Ou4c|cBiZ<@g%Fk#Y+68hRk{5WP&69=Q(PIkt+7JxJSEl%CwI{L|;JMAfp@PEtl)7Cai%4Yk(b+Jkk9`F6#;| zti|26c<5vS0vC@N*Hw6jAB#4MP1Lb#dp#f#3Y$hS$5DO61i=+iP2A}yK{&}|6_fDO zg<|_;lZCE*l1<2H$wM#;k`Tm@AY6x&Q8y$|0ED`NxdCQ;Df3(+0o4H9IEGAa%(fz@ z$QM%oEXM(A;k*_b2nB(;fWk7qA=xLPH!)X&i;V$6I9RmXO}0S`$p<;BUDyN)31l)# z!eH%^8cJXWr2tn-_AG~9rX4Xo-dG=}oW=sb6kUsWil(e+tD%GRT1a$p3C|8oZc&Qj zXuA9ib!WU$iHhA2$;(;R7Y$}JpYZcZOM={4G3DN|mGS%q8Ef66StH1OfuVPm1rQ<& z=o+Sh`FuWME0I;ogipO68`JO-G?fnkZmORdnPAh2RRY4TqgJm z8h#*+{s<^QVi~4*fgryq1d)+k+!^v7!jgH$RnDBWCT>e~oxsnbTPMjVap&U0d|zvB zA$=}SbRrCqzN@(T79p`dI%sy61W{lDyPQO1PJ_g08lIPuWij<2^a&$Pp&QpEF_tVc z4kglpeejiS9w>RHXHJlppmPw9vr7;&4v0(-Utc0yh&Dcqt`13lIfZK9VNwk1&3CYL z_nz%BjF2m$7F{y5?y~9>@7hj=FGPew8DuHc%sHPLO}ibN752Sy8UF)>tVS4)p3|d~ zDM99)Y%ECFIK~?;l@0K6^1`UOxel7BVOsBd13WqAk2C4=x%Kr+_k7|lLWW7}(Ir95 z-bc5HD?59DLR_zj!1q8N*oc%Hf*i5ZWVu5k9$*khM*vYk&bv1J zRCHhfNU|{(j0%Ze*f=gJj%m;(s<8?7X*pLCNAL>xP1b$O)^z+#x?LV<0&v1LorBbIufhm zK$1^^zV2J3X6hKguzloC(UOj_wbq$f!p%c&vw7#0qH`H0u((Z-vhk1}>J*~G!nc%( zyEx@}?YVTnde}tT^*EX&Qqmc#`zFxo|T9kK0=P#@tbgBiFgg;PdOf8`|moT`Gp&BF&Y197^8uc6=i?K`CJh~N>A#f$cd&yWA6Z`_i zTIP5+83t_P@Gx_5dpG)vZktf#E{|4-^oUuc2M<{?#5sUcz73NTZbOlS2n7AJ0W~Wj zQ4Kg~lam?-m8MZL?d_zL6f%hw1hQU?;$sk<>EuEVavz8_)n@L56@b-=+O%-^CMdj)@?B2pAQ#1<3nN0Ed(!twBf<3mG>c_Tvf_Kp?F+B|d`gdT7-m zNRrLfgTe)!c_jm#93p|bAOx`>A(98UX%Y&V0(Px>fJg_AIV<13m2`C!(!7qqpq%Cj zgx+N=o)ku*7F!uen8U_H5Kw}pduvc7K`ypc-b%rtO9_-$zb8_F5)lKtut<4kPmnLA z^wa@JggwzyPDR3LBOwr$Y%GFMRKv286+|jF&=Q8SOD1DXVjj|4Ic!!UZvs{>ipY^? z@S^XR9H3_b9|!l$Y&}_ejATkDCARJJN+3kKCWSnKP`BQ1#O8?mW3<`o3oIdyWuI`!53xd3qQ>)L)F5NIb{ez z?4FLmw#;OO32r8&kjd0bwO61c*&(mCJC3> z@Rb-A+XN~2q8aWFq9{SEK8t9Spv!j(t#3U?pHmcAPu{>x&DI0tq;h(>BE)UWv2nv9 zE(2YM)psyUB{dA1!AC8@NrG?|0<9HcV(jAd=?+(V=X6`5Y3DRrpZNCIhUa*ySji-04S2p2v_tY{n}G>Z(zHIf{cyjSh6 z5O`8D$bs`Kp>=JU;v7N{%6KyoQH|w`REAOVs%`6kW})S#mXj}(&t-4VB5Z+>n5o_a z)BE%k8%+lhGM5>2j^=5^Ia3UQOm>459LMECw{)Y_&ysxJxd(@w)EJ~p=FxM2QihHo z$Rv&&*$WxmUL#v7>EX)(wGy)AD$5wGtI?u_EJ_w@FR0f=vdB)KhZY`z(29w>m0{@8 z+7P0m#}88Bbp(2YBpQ&WrH~CLQnr5ZVii-n3M^fM5>Aq7jDU%6%nos0fvyU zBp@=$JbePE3~dFJ;_RTbdYvRAP}YMUldTWXaTl`e+5%X=;FSc{9hCX<^5L3dT)IN} zPJm{DZkp(@)a($jX4Duw=Z1-|<}~O`!0S#6qlG~V*(Gv6yh;--s%K=hd_pl=T_N9R zAVGl>3yWYtAUYCsC{kL;CP>I|qKK98bt^tcYe;|OKqG;Ffa8tMS{dmf6$-@gckxsQ zr)e3oo+eNxJW91O5(w46vra4QNus8_uXwT$V$`pU967Pp$umvaNoN9N;lfxouP7F0 z$8uB4X9@}k%nAxePl8aQfEVWb9EEr&JCmtLub`A{eD4B>q?x5eb`D7|&C`rm630CC zaCe~X6T3)CdTVjq5Oi6+!5#xGc}|Eh+#gh)JVK-zc}jVLlz2Y99mspgXqb`=x{!m)x=IKX`H1voS}6%p9c;Kj z9FA1m>6Iyf#QnYVXxoHfM80pb9O6-+0%H?8&v3KF6^{7RT1x{MI1EdUh zJ(+1z`dj!w2pK+xP*A@b%9O`=i84f8mmCH&g#ek!|1m7u7snYEsYteAA45iv$?}2} z<-pe{AZ3$7X6Xjpp17RIsAw>pTP*)2;P0u)LDa#e=jk)|R3m;t)R)v6ARbH}^|Im_ zlI0$Sjm-`(`kg{+6Fjiez@P}RFE4`=aNSC$W&j@~ds=UD4djf)NhUZAJIl%2L3(j) zT+ASO@hFQhF=}lj>RBZN#*Jr+%Di}lDnb)iM5t=C&$S?CjRG2yBpR3(8g22$7)>QInZWpJsKz?-)l+UX2A8JZ;V zbWIGFCBeE7Y6O_2Y2$UY6cCL5<|;$0z3Js{9x38Ikcxny06I+tRSU?QX|qFcbpi_qq}Q1-xvAsN1o z2=Nh}#Jkh^hoflNX3V}DAwm!$(nD0ZN0*$`3oF;O=ny!n+JsC6mH79XdWhIrASY&P zyWWr-ht^8+8b)jp)88>FI4{EK03oGi5%@Kbv@8narMtmp)`9kYVjag5FQ$gqBod3m zgYrEXlo&~5g1jD2E@^IcYBFvHgC0c}tRvG3NSabT#DgLb1#+Kv93Hp;nirNMSla3{ zNqF!py*Tx7g*0BQAfvnLpg#a&#fnMtl6EHz2~7l7&1cbtxbaOHiKusImn0xki+Md6UnBz|sd%M2W)p=(`p_4Fu>3+|0GXawmy5~u zI`A+dLj5ji@puByjl|MNQY<8}<0!lWE0OwO+P%|1gV`X79yoM;2r?%*>p$m-Fo@Yr z>i!bXr2DHP7E|XU=)4Z3p2eJIr}rz(BnSF3Yn=m<^a4r9q?jIeflmT52$k?^E=QA= zjO1Y)pGdW?A=YrO5=t=h!97Su9Hh8L0yR9g*^XnIa+PHgDG%AT09I<7?*bHZ0NU-- zr$))^EOPfCh`A!Y&I<2xL=qbEa3HmTQcCTL28(}YNJaGAZ3?k9}KCYd2wB2_FmkchT` zR};}Li-atfElh#yZD803jk+mt}5=V|cnE=+pf^LOIz3GssOdgH69 zN`h#?$0}Y4J2CQLrs7aC0${|`2;hexb|&>~9OBw0=g|}a-9h{^K|Ezd9Pb7!frEeq zSiKsOodlw(BTB-Ot>Ti5{32nPI>>Dl=uSURh3}aI|xEmz@yqax#P%g z;zU|~nH{Ug^+Ye7X%$L{f>9nzj>0P$mYIhyt)7Mj*Vj*I=0~A|IlwTLg!FP)^5l#RA010qNS#tmY3ljhU3ljkVnw%H_000McNliru;S3%CHzQV>erx~$02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{027T#L_t(&-kqCktes_*$A4?> zz0c{D-Z(wIB-X^%7OO}jNThTI;w{iJQ%S@qH*JOwL{bGWVKf7bCd^=rMq`2@aXKco z8pjwuXcIvR5-cBD5`_vJ95b}g(w265dQLCr^z3)7`S4uc=iPhPsqW-t@AJO!P3)N(%?t4wyNrVIoje9S>5-gQdj*m>BBuvssW}d_GL%h&t+F6Xw=< zUPQng6KiZHJs;+fhQNV55AH}_Z+zYW4eNI|EYD|{hj$gB?MXT&&B22N2qQbLUN6YN z#lz%yn5z_k0xq}c)OFVmNY)i?^yzw(Sx8cuZUn$Bgo96hNE_BiE2Mw z(&Rlb%o?zyQKGjcn~A&OqJ1`*G)J-=>xH2-P}93ERUxXY5OR$)L~0w3bGI{$qh=*M z88r}XDr+Q26A4fS%eG_DNqP2?rA8vGMYD~Tum`0wP>~G#+NP5lo9jvlQVsU(0;;un zgmoDqq*z31)Udl>P@*K8N{jF)l{k2Yrx~V#670Iqrd2_rzi`Vo2^u^)Au<@+n9nf8 z6~%+c&zdebC#csDx6*84C`^&1{TFS=gJdY+CZebW$v5Gt(Qy#GdVorgBa1NWu}K9? z1;J6T5m3V0ML`{K#J~!8D~hS1rkHqNv@x$urX2!#f&D_$Vo5TRrJEcX)bLFR@#HYn zQTGsz=+|_P2xa^xhMO2?`y5nsnBlXBu+zgKGNxn$O^twhNcXhI5j8U^kVdALGAfVR)1Yy~n#%}Nj_Way`T(Q^8&*Xql7z0uq!_1vKm*L?df~gma@R*Zp$eWYz zXgI%!geaiLyzWZnavRJ9myJ zpL~+Nd-pOvKAx|e8QpG|dGqFR*=3ipb?a8%`ObGTH9eIR4p)mlDjXf%tuPyK33;)4 zzpqHADmOvJ8xAszlT;}AKoK--79}V+G2?<@dl6oxgYl4jb82FOU;p~oJp1gky#4KO zXUpa-yleft7#$rAT32OiYKpP3G4|})!-Ee#$jTKf`OLP@uzL0CluMvub*gFMz>(v0 zoZpi{7q%FqvnF3c*}e9eL=%2faLyhXf%c5TX(f5Y0pQ?Af!~vu6*x zcJ1QE8*k*AYp!AO;>B6|0WsmApTY?@jwEM%k^p$O8VUTyT8BgA@IgxCFh}yE7|Qo{`liO@x&9{fB!$T zVZ#-4IvpCmK=L5*$R6&s+ig~^T**If-O4k+eVSkW>Its7<{H}VcFujvX{8tg3D9g0 zwYlq`?qcDB1*~1W7WaDX64&R&TFdK%;)Df=WaGE|x&AZEj83P+?%lij??3;U2OoHl z#Y+}t2UTvk-D)$`>CkC+&{i-3voKG$+od}*!!k_*!ANd^ZfJAbLPw$%Gd9I|9gJ<%U`l&$r841-O5Kl`cbB)rchVToH@g-x82Hp z_uj{oPdv$oKJ=mDAfu>xsH~KkRx%q8p4iRvIlDY^S~`6dMF2BH-Gj8<96UkK8t_(C zp4;;r*Is)qqsvCAsw%_f%$YNM?dxCXnP;Bq+idwdapD9&_`whO-uJ#&KabIAwb{OX zJ3DvoIAddD{QT!X=gl|Y+|N7a_ICSU`f9-4c?%llj;*0r! zKQ%SQO*j1myLa!V)9KU>Dr$IgVv@V=x{JB<=CNznE>^8t#rEyndF-*rIC$_NU-;q| zsH|!_xQI?-%`Tx<8{RBcH?o%u_x|vQKk&Zyy$>=>jPdbto`3#%{?^ydojaMCnaOjE zjxJ?vY>aNzZ7h0vdYZ3(^{d>pRTWphY>nsHuy(agf;+&Opd9OL8TtXZ=r+Fqzk96562FWPMRn%`>ifqC=zz`S|P zZ?yn8dh{sAj~~x-&Ye4#IdkUl%FAO}=Nd(e`VPT~!qiJn%2P z@WKncdE`ha_w;PqiO;yxb_-`mv0iT&Qn~5rY0Qj8i$cs55!&rGue|cgMQwJjD*wK6 zC2MER0$^-viZ8$M24|+HIdS3yBO@aKbUGd8&!5lh2M@Al&1%eyR;$Gy|G1wIfB3_k zJ$p823(H7LN)l17yz)wp967?$rAy-htiQQX*l=yXwWd8M|HH$>IesSlJ%0T7MQwPr z(_!`SFf;DVxU+hAn9)uLfMdsw1sFzc%%4A>Qxm76hmGF-e@}AhrI*s}cJmtQu;e+J zS+Zma{;s|{CfF^K2T&~*2M9NPc)>c)+x*Ni*eqBPWSm@)LE(AcfI zEE^r=;K75%b#ELXQZDe?Yp=0*@nWp1az+;7bkkHd$1At!(dtw(ooT11rm3uUCRJ6j zWXY0?+VJb=&-3)@)0{QqtQk+AKF#ar&jSR_SqM#4RWUw3&Z0$&vVf~@#kzHu@zhgK z^*h#>qay)r`}gl>WMm|6EN(WQu}THjUfL)&TSL0tZZkAI#Gyln!z2;rYu2o}s0};t z%_B#+`@jM2K5&3<9yx*o!^6WYT(~f#lQA?+PEN9F)v5*rM@N^kY}qot^{sE^Y{_G| zadWIP?!W(jZn)uwAgpAf7OEd!Y_`s{V%!UZ(a3Z&D{8DY+x zITsyvr@1BO@a$T(~f&k`oiBFf&%IT190|)A`C*zQUnHhj{qmf8*f6gH%<; z#KZ)zzWOTn-g__0moMk;yYCKmUgQ&`%@$g2U3uk|52d41%DBWG7sfT zH6J9ldGlsQM@Rqa&3^EMA7t&?wK=Z0TFNhe@e8iI?mDJtrnA>Botmn$>DoVCmAO7roi5ufCein>RB(HI)zVPd)V%Gcz+RU%ot^XPeO?opHNWm$PTj z;$}=uO>yqrxsZ=Hj)`?<)!0{rxq1<+nqjEkcvV%r;~nqdt+(D{-@bkIc@$@8Xo!36 zxreK+x{BGeXJ2rm3l}crmRoM&_SK-aO8pJsayyPNy@(_rL#rwrtrFoK;nYrn?f) z!h{CgfDS`G8+(7T-g7C^8}x_VA5I;HTsI!X*`Q+KqJ`Xi^UeJ5hd<=bJMUyFM)y&(BcS&04J%Z@lpak38}SYu2pc&O7hyKh~8nw7?T6 z?+pzN)s6O;Gk9+rnIWE1=9tNS`qQ7zJgrm)l~|%xO`NP$stuBCwUpDRPxI5C{*>Y2 zVLtMakFa#C0xq&m(QnibGmoO9d~%R8Eeq-VU@pB9}e%88HWzN!P8IwmdVqTyzOnPShIRH z%a$!;czBqZZkLHur#NunHO9tXWqNv=O`A4x)%&mFlKGcpFJ0E1GciqzHHgN7qJ+Y{ zXy3Z>=lD!ton1hrh1`Do?cOWV=9hH9rUQWt6M@hcI3=9n3j`T9-ut-M=q?6o|G;pynxzrTJfkph-`;`lDNm7}( z8D}-OXH~r5e1!j9L8vg%Sg9$LAsz(iu_>rc`a|d*N*tUEa4S6Wp^C+&?xL%L2ch+~ zI#u!59qf&hx!A6eh-w2iEsiC!YKZ`oSbQg=tQKbK6dLBOOkt$i9}X==&smE0v@ghn z6EA9CxpqW=xwzyOWiLYj37n~ZI$+(RrKedQ022DcUbcZ$W6jEP=dO=X8Yvt_J-VNX zm#75pjwubeBuy)t6P3o`#7=%sT3Tm5B~7qo@&#ccfm~({cm~!`Xlm0>i?omtN?L>T za!De*DN^K`i5CstLk=70QcZ_J;z5~XJv`>jw~C&zk5D4PEJSFK$`ZpsqCtg%zyKzm zODMONjz~dbS{d$Y4}r!o7jTdLvj!6{N;J|8P;U}Va;ej@#Cy4o*6SsEV5q4yQ`&0D zFpyxEUa*O3eioQZB&B(Sl;)8UC+gYE8jdUx+PqaV@rndo39MP^Z=tFPu1c$DbT_?s z!Hw*JVb*BmU-yQLny4yGzH>vEI~6has4}bYkB2!doOpn@C6U z7!|zs(gfb5=s*^1&7Hd8FTKBED9T8+ukZ%>oI+D& zrk5IK#KxL5D8&KxDJ4%F&33Ps47`YBbAvQ(;K4S^A6{50$3%oA+sQD_xpTDX(pSVh zAkxdTmkruUfle?n#I)gvtT=qoLxxRpT0NZ3vWBxaiv=`^p4SIZf0H8;lbEO%HWh^s z&kXS(5*Ji5?#%~}s~(#G!6utOKd2#OeVdldiO#h!rOEn@IY2vUG7vJ!K^`O|-RY+{ zMQF+PDb3$IDO$sq+F8;2eERJ}@AZ_a5wp3Hog?+S$^%3Bd?2IJ5JH1T?au!JZ8Z`= T$!wV;00000NkvXXu0mjf?GYYS literal 0 HcmV?d00001 diff --git a/app/res/icon/ios/icon-60@2x.png b/app/res/icon/ios/icon-60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ed91ebab8278c228a75fb2a20d48f84a11979141 GIT binary patch literal 12656 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 AfdBvi literal 0 HcmV?d00001 diff --git a/app/res/icon/ios/icon-60@3x.png b/app/res/icon/ios/icon-60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bc32c313365d46127b07b707270c36e7dcd01112 GIT binary patch literal 23504 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 Date: Fri, 29 Dec 2017 21:43:54 -0500 Subject: [PATCH 305/414] Fix build and add splash screen. Use a build.json file to manage certs in ios --- app/.gitignore | 3 ++- app/config.xml | 20 +++++++++++++------- app/package-lock.json | 11 ++++++++--- app/package.json | 10 ++++++---- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/.gitignore b/app/.gitignore index 8973efa..2a65dcb 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,4 +1,5 @@ *.DS_Store platforms/* plugins/* -node_modules/* \ No newline at end of file +node_modules/* +build.json \ No newline at end of file diff --git a/app/config.xml b/app/config.xml index 3192589..f88c33b 100644 --- a/app/config.xml +++ b/app/config.xml @@ -1,6 +1,6 @@ - INTVAL + INTVAL3 Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras @@ -22,6 +22,10 @@ + + + + @@ -29,16 +33,18 @@ + + + - - - + - - - + + + + diff --git a/app/package-lock.json b/app/package-lock.json index 59fe73e..53503a4 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -546,10 +546,15 @@ "resolved": "https://registry.npmjs.org/cordova-plugin-dialogs/-/cordova-plugin-dialogs-1.3.4.tgz", "integrity": "sha1-XMlm7nyZsvW1s934SQAmKLDacVc=" }, + "cordova-plugin-splashscreen": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-4.1.0.tgz", + "integrity": "sha1-gQKKt2Q+YVWT0n8q0CRFYR8ZRrY=" + }, "cordova-plugin-statusbar": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.3.0.tgz", - "integrity": "sha1-MU4dAJxlkQzf3gNL/KE71QLJ5eA=" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.4.0.tgz", + "integrity": "sha1-JOspc3ldEPbxrjIC90+Ix9mQzyA=" }, "cordova-plugin-whitelist": { "version": "1.3.3", diff --git a/app/package.json b/app/package.json index b896cc6..8d5aace 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "com.sixteenmillimeter.intval3", - "displayName": "INTVAL", - "version": "1.0.0", + "displayName": "INTVAL3", + "version": "1.0.1", "description": "Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras", "main": "index.js", "scripts": { @@ -16,7 +16,8 @@ "cordova-plugin-compat": "^1.2.0", "cordova-plugin-device": "^1.1.7", "cordova-plugin-dialogs": "^1.3.4", - "cordova-plugin-statusbar": "^2.3.0", + "cordova-plugin-splashscreen": "^4.1.0", + "cordova-plugin-statusbar": "^2.4.0", "cordova-plugin-whitelist": "^1.3.3" }, "cordova": { @@ -27,7 +28,8 @@ "cordova-plugin-statusbar": {}, "cordova-plugin-ble-central": { "BLUETOOTH_USAGE_DESCRIPTION": "INTVAL intervalometer controls" - } + }, + "cordova-plugin-splashscreen": {} }, "platforms": [ "android", From e6ce3cd977c426dd60d872224a9186529d63734c Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 21:44:31 -0500 Subject: [PATCH 306/414] Script the creation of icons and splash screens from a single source. Splash screens still not working --- app/hooks/icons.sh | 26 ++++++++++++++++++++++++++ app/hooks/screen.sh | 14 ++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 app/hooks/icons.sh create mode 100644 app/hooks/screen.sh diff --git a/app/hooks/icons.sh b/app/hooks/icons.sh new file mode 100644 index 0000000..d63e9a1 --- /dev/null +++ b/app/hooks/icons.sh @@ -0,0 +1,26 @@ +#!/bin/sh +base=$1 +#convert "$base" -resize '29x29' -unsharp 1x4 "res/icon/ios/Icon-Small.png" +#convert "$base" -resize '40x40' -unsharp 1x4 "res/icon/ios/Icon-Small-40.png" +#convert "$base" -resize '50x50' -unsharp 1x4 "res/icon/ios/Icon-Small-50.png" +#convert "$base" -resize '57x57' -unsharp 1x4 "res/icon/ios/Icon.png" +#convert "$base" -resize '58x58' -unsharp 1x4 "res/icon/ios/Icon-Small@2x.png" +convert "$base" -resize '60x60' -unsharp 1x4 "res/icon/ios/Icon-60.png" +#convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/ios/Icon-72.png" +#convert "$base" -resize '76x76' -unsharp 1x4 "res/icon/ios/Icon-76.png" +#convert "$base" -resize '80x80' -unsharp 1x4 "res/icon/ios/Icon-Small-40@2x.png" +#convert "$base" -resize '100x100' -unsharp 1x4 "res/icon/ios/Icon-Small-50@2x.png" +#convert "$base" -resize '114x114' -unsharp 1x4 "res/icon/ios/Icon@2x.png" +convert "$base" -resize '120x120' -unsharp 1x4 "res/icon/ios/Icon-60@2x.png" +#convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/ios/Icon-72@2x.png" +#convert "$base" -resize '152x152' -unsharp 1x4 "res/icon/ios/Icon-76@2x.png" +convert "$base" -resize '180x180' -unsharp 1x4 "res/icon/ios/Icon-60@3x.png" +#convert "$base" -resize '512x512' -unsharp 1x4 "res/icon/ios/iTunesArtwork" +#convert "$base" -resize '1024x1024' -unsharp 1x4 "res/icon/ios/iTunesArtwork@2x" + +#convert "$base" -resize '36x36' -unsharp 1x4 "res/icon/android/Icon-ldpi.png" +convert "$base" -resize '48x48' -unsharp 1x4 "res/icon/android/Icon-mdpi.png" +convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/android/Icon-hdpi.png" +convert "$base" -resize '96x96' -unsharp 1x4 "res/icon/android/Icon-xhdpi.png" +convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/android/Icon-xxhdpi.png" +convert "$base" -resize '192x192' -unsharp 1x4 "res/icon/android/Icon-xxxhdpi.png" \ No newline at end of file diff --git a/app/hooks/screen.sh b/app/hooks/screen.sh new file mode 100644 index 0000000..ed733b5 --- /dev/null +++ b/app/hooks/screen.sh @@ -0,0 +1,14 @@ +#!/bin/sh +base=$1 + +c="convert $1 -gravity center" + +# iPhone +$c -resize 320x480 "res/screen/ios/Default~iphone.png" +$c -resize 640x960 "res/screen/ios/Default@2x~iphone.png" +$c -resize 640x1136 "res/screen/ios/Default-568h@2x~iphone.png" + +$c -resize 320x426 "res/screen/android/splash-portrait-ldpi.png" +$c -resize 320x470 "res/screen/android/splash-portrait-mdpi.png" +$c -resize 480x640 "res/screen/android/splash-portrait-hdpi.png" +$c -resize 720x960 "res/screen/android/splash-portrait-xhdpi.png" \ No newline at end of file From f9f98ebc0220bd019b542cce5b6129899150b3b9 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 22:05:00 -0500 Subject: [PATCH 307/414] Fix direction label in mobile app (light up selected) --- app/www/static/js/intval.core.js | 3 +++ app/www/static/js/intval.mobile.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 87b5c4a..6663b14 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -154,6 +154,9 @@ var setState = function (res) { document.getElementById('dir').checked = true; STATE.dir = res.frame.dir; setDirLabel(false); + } else { + STATE.dir = res.frame.dir; + setDirLabel(true); } document.getElementById('counter').value = res.counter; STATE.counter = res.counter; diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index b6c1883..e3dc43f 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -181,6 +181,9 @@ mobile.setDir = function () { mobile.dirSuccess = function () { console.log('Set direction'); mobile.getState(); + setTimeout(() => { + setDirLabel(STATE.dir); + }, 50); }; mobile.setExposure = function () { let exposure = document.getElementById('exposure').value; From b02035ea16d15dd572c1bb1493940e883de7163a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 29 Dec 2017 22:41:21 -0500 Subject: [PATCH 308/414] Add a universal splash screen for ios --- .../screen/ios/INTVAL3_SCREEN_universal.png | Bin 0 -> 110447 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/res/screen/ios/INTVAL3_SCREEN_universal.png diff --git a/app/res/screen/ios/INTVAL3_SCREEN_universal.png b/app/res/screen/ios/INTVAL3_SCREEN_universal.png new file mode 100644 index 0000000000000000000000000000000000000000..259a4f09ccd152afd392e036b5dbb78a4b82e77b GIT binary patch literal 110447 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 P00000NkvXXu0mjfm9ZO% diff --git a/app/res/icon/android/mdpi.png b/app/res/icon/android/mdpi.png index 58072eec19512c24c6485558ef948b80254ab6a4..0c506a743776ef9629ef89361403cea1267067aa 100644 GIT binary patch delta 2318 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 delta 3058 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 Ai~s-t diff --git a/app/res/icon/android/xhdpi.png b/app/res/icon/android/xhdpi.png index 605177fabbbbc0be714862333f356087dab5ce98..701cad687da74c9e0cc9d15576791546c3531012 100644 GIT binary patch delta 6358 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 NNI^zbx=PYC=zn;Tw|oEq diff --git a/app/res/icon/android/xxhdpi.png b/app/res/icon/android/xxhdpi.png index 360b7a1fd10e79d7b5f89e5c5656e1ae42aefee3..6a6b208dfa31373f11bd8bfe4f2b828f7579b526 100644 GIT binary patch delta 12663 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 FV1jSncw_(o diff --git a/app/res/icon/android/xxxhdpi.png b/app/res/icon/android/xxxhdpi.png index 5c416a4bf9f684aedd159d0a23d78f277b0c1d4d..6307a727e499a24e4a67957e02126c248ef0a49b 100644 GIT binary patch literal 20457 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 z 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�*}XAT3T9JSy@|K z+t}FH+S(Eb1UoxBdwcucyLUS{I1q`%J$v?$NF*mGCo-Ar?Ck97>Pn$ds8s5K0|z`k zJ!v!=olf`j^J6d=2M-<$3=Cv4nL$B8!NI{S7AqtqBs4Vi@ZrN@VPWCn;SmuLY&JVG zGBPSEio@Y>xm+HP7ZVc`7Z(>FAAjo9sf2`t#Kgp;q@hyLYd( zwY9CS?ZJZwot>RsU0qVCw5O-%>C>mZy}f;Xef_4U{b+Q5fB!Qqc3@yYMj*&!vcbW@ zp`oGS;o;HI(Xp|yaSCOe!5AMOpO~1KWU(eEC#R;SooIrnHcPirj*8rfc&G4m#1@tJzuc8K7$ZmPV zHOrsGgai)s;!&hd^4_7Ihq~YbuDy*xA#EO)f%{asnFw1gi;Ms~FFnYGI|g6()QO7r~rxwSPW{>Gdtn&Xfc^J2n(Ps+Y-Z`CzH8XX=PpIVhzWjchw{?p0{`YEaX(WiA0eoEkF5hO=tXRhK1?BUpQ?0w~ZuwjFMjQth>gNa+P9-8KCu;>w?QL6q@zSBa!m9(D zy6>x=|1DBcjW*;$hwWRn$o@^sskmf;(jsoq{02IW&Q9|=osbZnmzvp7wLH&rUjMw$d6F&7joH9_U;qHvOic`}sWtp>GJ&Ws z`A3bwW%ye9`w{%#M8vu3;-e}0U+)!0Q^H;#jOB9C=>uF?f?M80|0MO z!5b?rD!?3Mf(iiu7S6v(1LPH)r!E;Wre<))c~&7hMi`~TBoP2;=Pa+C#4Z(v|xXlMwB!;OrLjE#*=OiWBoO%VtL z5{bNe^{SbfnYp>Sg@uKsrKOdXm9@3Cjg5`1t*xD%oxQ!iqoX4Vg>rIoa&~sUcI}#r zi;JtPtDBpfySw}K>(@O!J<({ix3~9=8#jD>e0+U<{rvoH-MSSJ5D*v`c>DJ4;NV~k z1``$*79JiR5fKp?85tE7g~ei{qoZSDV&dcD6A}{c-o2ZYlyv|8{Ra;oBqt|7eE9Iu zqem$zDUTmNe)8l=YHDg)T3UK~dPYV@W@ctqR#r|<4i1OIEp2UW zZ{NOcZ*TAD=;-Y1?C$RF>FMe1?S1#|U0+|{`}gnr`};q9`0(-L$AN)?!NI{#pFRx@ z4Gj+ukBp3riinJkj*f|ljERblUA#CZESq1`;x!^($doM^70B6yAl(#va+(ex=P8+q);e7Gctbu{JBbYDw?<%Qf?uM6gOO%)I~{n z#eCf-%V|Kuvky)$YJ-42M6~8w`Jf-%%#zMPk`&@!El41T+7i*?2)-qH$SZ5Ttz*WW z-hz6S-lEnH@3r0W?IX<6r){nEZS2$a9|tkQuTCt?6o~X~?!3PeTnH>--Tyxvxzgp= z=2$`nlJnOk)t#gw#0p(zJdGJ74$iHETg)B~w`^HeHpP@Lln0Y_}W^9Vo7>OP$=2H@XQWkjXkzWx`{9B&g*9x@Ko1e zZvtOXeRUkaGnis&B6Y&4v$&6XZ7cJ+B~G^j98^Em(+B3tw08#;6u;c=EOKQi2aFpT zN-C<#66kjQ@vof{rWZSuu)e(f3Iu8)mWy`vbhR$7?Y zwvOycDnIL3AMzbHx#G|2!-AeJixd^AW>%ltm*n-a94eWgynN}PZlN`DJA>UQY(MV<4W{!b$x-X?j#Ipmk`btl6bxOfnh zc^p_~iZL@KxNA1G-l3#1s}SD3<8|*)D$b*gf5ZoBqcmM`eGk&t zs)b+0Z*(~#YZgPJ9G~x13f|Y!Y`OL$PA#Nq*IrkFX6Zr7@~HjruGjnX+DH8lzQZ`o z5^`>u5?+H+1`X$5ZZ>iLDvTb{-Wd%!oTa&?TWp8zKOs7!UTTeeer}xWpGDUX;usE3 z-AI2@fnDn`7K%MeKXe5V-Q!1Y*epRvOoj1S+-vx?nhf#>Cc=XCF{{*X=RdkR=A{#< zGp9ToYNV1=+a<`B{)d%Fo@=pUnux?+&+rLr-{_CbM*}mf7+9hC+%Y)Lqs01k)~Vy3 z7emsU2<`1GdbP(t=9H1~i8F~)FSE_b%fiEP!ZAW3SthTnU+Xn%hqer!{FWo?#sm$B zkw>leqs0*8oYv>DAIW|v9E8p^Tq4T5+AZ$WuF#q^w`=Q(Z=Ti6I=t9_F~+#6=H!bCK=_>dCIoS! zr1*(x5xL5I3T3m$Z*SBrPC5}1-Q4b;NXClh4hg0Nb5AMnK6eSqE!7($z>6<;s~UBW zyKM*r_?A^iPLth0vAPEA*(qsP2IC{(;C{j9meTg>2X2Z1!Ba#quy|-vGR?cc0h5i;d;z^E3e$aNf%9A)zfS# zjknFvs_}mNwc&02w1SWwA;oqus`RCW@8P?I_3G0;y}7U1%Yuu-!46QYn!op?-#S5x zBlhVx`8WUN0ROgwb(f7(K=jHp=qHsL52|5Zz`z|b27&(m=l~305RCS~puI%y_+V~| z7@L||I>fW{Qb`hjNnTV4=J|gNKpCo}CaRdu3BqXGy zq-11dH*ellP*6}* zR8&$@QdU-0QBi@xV5+LBw{G21Q&Uq{SJ%+c(A3n_($dn_*4EL{(bd(})6>(}*N4O5 zw{PDzFfcGQG&C_WF*P+cGcz+cH@C2`u(Y(ava+(awzjdcv9-0ev$M0ew|8)GaCCHZ za&o$J=Z>?pvx|$1o12@vySs;nho`3}0)g=M_V)4d@%8n+ckiCRzkg6rP;hWC5{V28 z3kweqkBEqfjEsEn;6ZeBbWBW4Y;0^?T-=ilhJ5&N=nMpr%#_f zd-nYK^A|5(q^72(rKP2(r@wspG9x1cgTZ8GW@cq&WoKvS0t;OT(_7JzIAqXc6D|2 z^z`)h_V)Gl_4oG=3=9ko4h{_s4GRbi3knVk3x5|D9vK-K9UUDT8zU$v5MVF@fiONk zJ|QkXF)=ZzpfIVVGzo)Es;f`J;gge-Q-+39Q&ZE^(=#(Ov$L~vb8`y|3qOAR`1$ka z;^N}c($X>lvAn#zva+%o6}7s$y0*5qzP`S(v9YPW69`$1D-N{}d)E~1Akg}&fXF8p%CU-ZK zyTO!08P@?75Hb^tok(YQCykU5_c8qqev@b}_Z?P(U=m&+nshNVRY9X1?h-M}e;>@P z)?=}g$2)ze2dme%xkO(TUc<*)4}t78BoGwk|7UWNOM^{Hi1yFy-KOn?&Xcbp)wP9? zYl+XQ-BonCIhhz~im9N;nH!kvHelP231>B4%dryiwqIEJ@`n1Qej7IvC6c>rh{gxW z?<0mZM09j#GdgEly!SEv4)-_OesKz?aa#_FrG`X>Af-c{BODED+~{~h2e!OIoDcFP z(l}v5#!9}bKy+>EDl4Oj9Jbp-Hrr5nr9Ls-WMhh??WFdLl73u^%h%M5RWnL(A+sROHkhCDVd%G)- z$B5*uM|2Sx=85_|byBv?)M7nAD@g+N68Vc#GqYJkooOs2Rh4jtR+IBm@ZzcH>5;ZO zR6F?a_ZGngeH1&HyPr|d;0i;Y!4Vrm0wXAYX5V)^L*Ch>ed+p1iONf%QL)3wpmZo* zmcV+vR<2H7DDDRg zVi(x-8s|aBWJ4?dV5DPy?8ObIC!J2R$eo?`z{qv6vv!pL1Q$&Bcf4kAXj?=32&wGP z!LMn+oLUp4xi*EgB3)NAih<;za6iR6jx))UrR*OR1kF;Cgm5luNwn)@LGwZ;S zKU2YH1^e-KH*aqIV%#}eP!uXE9RuPNVChY2qw#!<(FUkTMUD`d<=v{`z7s#91^gy{ z>Pl-3ImRFxso>41UMyxoh44~=jJhbre$aahjxIl;+PuYtkj{uH+4`x}$eabM&@uct zyEn8)4Q6_6;2g(sEQ%Y49jn63|5|&a4a37PJh@_y%WsS63a_bCDt_|}AF+@TF-+w6|QSDo9c}XVJyI z--kgTa|6n#ky-LxF;b89GDraYX{c8=Vk^XxQvu)(LZ`F_~I1aVM+8O zX1_WOg|CmY1|Cy6yfGf=y;uwcC30cjIa20{oy|1NpMdWLe({M7%cK9rW!N?wC(US3y*iA-1~SF;=3hPN=18u=a_?tOq8nW%ibe7kSfhU@)K8x~;0l`M|7@?!&4M z436d`D69B%PHmKYg{>g^NauV+{c z^QDYygKDimFSjR}VAiPY^4^%a!%tn=7ABV=^#?l&V1r3yup3?O8ChzMzrSsay|-zt z{@F3}sD*G=*i1StUOhdkziO;6pQJi#A z#cz6qTLr*AS&*Dy$M=USThmp;S_U5A9;ffSR^Xn{d%@Q-AOlup2Z}AFh@2=xinYx7 zKCRZ{;G+pix5Ck&u8_d^I-vty1d#wlgK#*?#)))-Rt!+qaK-yPw1j~CPRY$$!8@B{dOnKAHj1g_e&?0b76~9Us8zr~( zLFl$(W!neMKGLF1zluuhT1UcN>`zm`agx&a2 zFjIHL34YEi8Y+*9l8RVtp!%Rgluq(N-?y% zV`i8fw0sS~dK-J2x;38Sdn+5(YG>JohYsQ16h_)SdiQM$_%0tq)K*d?i*Fi1+O@eQ zM#f3^lYRK4Wv(wk(G|y~IBWmBCMz1eAVtUz?22fKf3!`u_fSV3bzbRlKQ-%HI)Q@p~89JlMppd6y{uQB#D`H(>vi&Lo@L9Nu-$#js?0 zG$!5~v|D?g>~WV{R8oz+RPI4M9Nlfc&?C@MagD0-doXVmelNx0Y;}(`e-&j=vKD~h z3Nq+sK&`?u+zZJdB#aCEkw#d$a*g zViV8yl$fk2pq!cZOi`zqxyB)6ExG&Aw3N;ETzMfrbT#&A{(4!dury%_uI7w6eb?0g zrO@b_B*UcXsfR-40Q z0-&7K<=)t7It)Rfa&6 pb#(yzfT*?*d)A-zt{^0Ip-KU7_g``yadv)4~7% literal 0 HcmV?d00001 diff --git a/app/res/icon/ios/icon-60.png b/app/res/icon/ios/icon-60.png index f51677194d4eccc7600dea1c2367695f26a5546a..e537029ebf85af096e07adf4ea6417ac8f5c2404 100644 GIT binary patch delta 3753 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 T?Caf000000NkvXXu0mjfknUuk delta 5103 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 AfdBvi diff --git a/app/res/icon/ios/icon-60@3x.png b/app/res/icon/ios/icon-60@3x.png index bc32c313365d46127b07b707270c36e7dcd01112..9091c339f257aee4dcf81f3c6c367aec68d47663 100644 GIT binary patch literal 17617 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 literal 23504 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 Date: Fri, 29 Dec 2017 23:21:00 -0500 Subject: [PATCH 312/414] Snafuu with the icons script, fixed the path names to deploy new icons on command --- app/hooks/icons.sh | 16 ++++++++-------- app/res/icon/android/Icon-hdpi.png | Bin 1920 -> 0 bytes app/res/icon/android/Icon-mdpi.png | Bin 1526 -> 0 bytes app/res/icon/android/Icon-xhdpi.png | Bin 2472 -> 0 bytes app/res/icon/android/Icon-xxhdpi.png | Bin 4415 -> 0 bytes app/res/icon/android/Icon-xxxhdpi.png | Bin 6004 -> 0 bytes app/res/icon/android/hdpi.png | Bin 4787 -> 1920 bytes app/res/icon/android/mdpi.png | Bin 2409 -> 1526 bytes app/res/icon/android/xhdpi.png | Bin 6417 -> 2472 bytes app/res/icon/android/xxhdpi.png | Bin 12672 -> 4415 bytes app/res/icon/android/xxxhdpi.png | Bin 20457 -> 6004 bytes app/res/icon/ios/Icon-60.png | Bin 1643 -> 0 bytes app/res/icon/ios/Icon-60@2x.png | Bin 2740 -> 0 bytes app/res/icon/ios/Icon-60@3x.png | Bin 3700 -> 0 bytes app/res/icon/ios/icon-60.png | Bin 3832 -> 1643 bytes app/res/icon/ios/icon-60@2x.png | Bin 9324 -> 2740 bytes app/res/icon/ios/icon-60@3x.png | Bin 17617 -> 3700 bytes 17 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 app/res/icon/android/Icon-hdpi.png delete mode 100644 app/res/icon/android/Icon-mdpi.png delete mode 100644 app/res/icon/android/Icon-xhdpi.png delete mode 100644 app/res/icon/android/Icon-xxhdpi.png delete mode 100644 app/res/icon/android/Icon-xxxhdpi.png delete mode 100644 app/res/icon/ios/Icon-60.png delete mode 100644 app/res/icon/ios/Icon-60@2x.png delete mode 100644 app/res/icon/ios/Icon-60@3x.png diff --git a/app/hooks/icons.sh b/app/hooks/icons.sh index 8ed9976..8ff58cb 100644 --- a/app/hooks/icons.sh +++ b/app/hooks/icons.sh @@ -5,25 +5,25 @@ base=$1 #convert "$base" -resize '50x50' -unsharp 1x4 "res/icon/ios/Icon-Small-50.png" #convert "$base" -resize '57x57' -unsharp 1x4 "res/icon/ios/Icon.png" #convert "$base" -resize '58x58' -unsharp 1x4 "res/icon/ios/Icon-Small@2x.png" -convert "$base" -resize '60x60' -unsharp 1x4 "res/icon/ios/Icon-60.png" +convert "$base" -resize '60x60' -unsharp 1x4 "res/icon/ios/icon-60.png" #convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/ios/Icon-72.png" #convert "$base" -resize '76x76' -unsharp 1x4 "res/icon/ios/Icon-76.png" #convert "$base" -resize '80x80' -unsharp 1x4 "res/icon/ios/Icon-Small-40@2x.png" #convert "$base" -resize '100x100' -unsharp 1x4 "res/icon/ios/Icon-Small-50@2x.png" #convert "$base" -resize '114x114' -unsharp 1x4 "res/icon/ios/Icon@2x.png" -convert "$base" -resize '120x120' -unsharp 1x4 "res/icon/ios/Icon-60@2x.png" +convert "$base" -resize '120x120' -unsharp 1x4 "res/icon/ios/icon-60@2x.png" #convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/ios/Icon-72@2x.png" #convert "$base" -resize '152x152' -unsharp 1x4 "res/icon/ios/Icon-76@2x.png" -convert "$base" -resize '180x180' -unsharp 1x4 "res/icon/ios/Icon-60@3x.png" +convert "$base" -resize '180x180' -unsharp 1x4 "res/icon/ios/icon-60@3x.png" #convert "$base" -resize '512x512' -unsharp 1x4 "res/icon/ios/iTunesArtwork" #convert "$base" -resize '1024x1024' -unsharp 1x4 "res/icon/ios/iTunesArtwork@2x" #convert "$base" -resize '36x36' -unsharp 1x4 "res/icon/android/Icon-ldpi.png" -convert "$base" -resize '48x48' -unsharp 1x4 "res/icon/android/Icon-mdpi.png" -convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/android/Icon-hdpi.png" -convert "$base" -resize '96x96' -unsharp 1x4 "res/icon/android/Icon-xhdpi.png" -convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/android/Icon-xxhdpi.png" -convert "$base" -resize '192x192' -unsharp 1x4 "res/icon/android/Icon-xxxhdpi.png" +convert "$base" -resize '48x48' -unsharp 1x4 "res/icon/android/mdpi.png" +convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/android/hdpi.png" +convert "$base" -resize '96x96' -unsharp 1x4 "res/icon/android/xhdpi.png" +convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/android/xxhdpi.png" +convert "$base" -resize '192x192' -unsharp 1x4 "res/icon/android/xxxhdpi.png" cd res/icon/ios/ find -type f -name "*.png" -exec optipng -o7 {} \; diff --git a/app/res/icon/android/Icon-hdpi.png b/app/res/icon/android/Icon-hdpi.png deleted file mode 100644 index 235239e893a0bd2381f6844c8b9177555e7035ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmZ`(X*ApW7X2Gyh#@u96hja*EvYeb5lJdR+K5~#Xq8Y(r6p#4MI(k9LQTsvmeSj6 zEUs3KrF6Kc(7MGdN^do0rKxJU>HGFRyuH>r=XcKD``cc>4AL1VDXDqe}@I&$wUB%*9CwiCIEcnpORJpAj%W~xPAbD zECB$;t7TnoC_W*674H=Z01|RPO8_V>Q{o>*83ZCubWu`WSQN?a#oq(~!E+>63XV?+ z2nYxY3JM7c2@4B@Kp+tj5m8Z5F)=Z5ad9vhEFmEwDJdx>B_%B_4S_(89Xlo?BO@y- zD<>x>FE4-m_;CdV1w}VlWsi7K_8-oSdBSc)YW-vx|$1tE(%KNIZM?tecw~nM`(fcc)M&9v&W^ zo}OM_UQ{a8+uPg6$LGR@3x0ln{{H?n8Z9s|FeoS}G&D3UEG#@coKB}lL_|bJMlu+T zsHmvu=;+wk*tod3`1tt5#Kh~@uO}rXrKF@VnM@Xom7boSk&%&^nVFT9m7SfPlas?{ zvkM9e3JVL1i;GK2O3KU2D=RCjs;a81t7~d%>gww1>+2gD8X6lLo0^)Mo10r&T3TCM z+uGXN+uJ)jIvzfJ_~_B2uCA`fj~{n;clY%4^!E1l_4N%53=9noJ$?FgczAeZWaRnt z=PzHr9334U8yg#kLdVC)U#Y84z+e*-6C52K4u>;2IXPu!HZ?W%`t|E+JG<%W>A#GO z{xUazi$cA1bbL!7%*@Qp&d$!w&Aof~Zhn4#VPRo$adByBX?c11!-o$mD=XaebS{^> zy1Kfywzk>Uwz;{vwY9a~)wQ#;^ZE1VT@Gh=cXw}bac^($%a<>_^>yCH29L+v-{1en z$I;ONA4f-rd>sA6lRr0uerC(x#R+R~7huW{wa_X7i>90#Tz~T^MlMiZWJ`~5>UJ&h z8T>XaA672fVR-r1DRONDb^`o9Q@<)HGbKf0N=;rO)$q5>XZ{8-!599Gf*?gIyAgF% zq(P-QVt&JoZ(}w_Y-0ZPu_A7>o0oD#u97UQMo?IX+Runy9-K#1eUbj&rXYrxX?Y$- zI=nX4$!-?O_4?{Oj3cU|~NjKyDI<{8C%WO!Xh-_c+17myU+*X0Uf35Wmy zaFiy9?opGoc#SzZn$`Eq-VADm3(sW_fJ3;b5Ei^lv+9rNScBN;8`Odcpd7FRs% z%8oq~#G-Lf_eNT{a<1%QN>Pf>3Xx3+W!F^rv1*zon5G2d={<)8+hg3>G&0HXE9$-*&8LNvB{Vt?=`zn%4!m;U*xQ?u!S6 z#7&b=ABoUT^*k!t|DzRb$)Xgw)k?ffZ4oZG=W&HO1Ux)#*0Yld_GG1%88 z#0$22KvQ|^dE+)*?R@ZnW_Gtn!ljpmF#l?G9BWzMFYL!umst`Hp=5t>34REv6Ppa? zgbm?29ge517xNhl&ii+g(|Z!qSS3TU7EXD)Sw&z}(?)|^fr7rgpl8Vs1C;)#!~i|e zcbb@*{Ov?fDDzg^n$K7-M>cQp-UrUiFbcrZy|tp=d7+vKoT#)`4KMuI>i)s5=bI<0 z+rve``qhHEDUByyA diff --git a/app/res/icon/android/Icon-mdpi.png b/app/res/icon/android/Icon-mdpi.png deleted file mode 100644 index d7912c8b20b0438a6679630dc7f8d32c0d5d0fe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1526 zcmZ`%c{tMz9RD$F=FE|6&B$%uX6~Whv1Mqi!c5L=<(hLIkL$JMbyj+M9UTf$61tgI zXi6bMdL6kQd2=k&=H`{&pZCA_kN5LD-}CeN{P)SDc(};HHQ@jN$hndTC%}o>D;Naq z!Q;}6;E*_rC*uL&aiPpkh$MKULP;mc0FYt;0PHLP*alzhc>qWRjr?E$z+D9Zwb(N5 zaR-o)jwR6&06<20uf%}sW$NG)%5o(Wq0_QR2`G-=N;(e!;--{iUPO=*6B82`7l%L~ z5)u-Ul9EzVQcx%q27^gUOT*!C85tQ_Sy?$bIeB?`1qB5~Ma6yl_9-bTDJv_hsHmu_ zs_x&vUrkL-U0q#6Lqk(jQ%g$=fj}HUAdpC;wzf73h0@W{(bd(}*Vi{NFfcMQGB!3g zF)=YUHASP*znGa}Fc@=lb1W8XVPRouX=!C;Wo>P3V`F1$Yinm`hr{9Q?d=^L92^}T z@p!zGlasTvGl4)L5{YhZZtm{xWHQ;))00A>czJnwdwcu%_)w`-UteDujppy~9~c-I z6ciL392^o7LZ{Oi3`SU3Sa^7NL_|bnWMouS6qCt}j*gCriHVPoPe@2eOiWBlN=i;n zPEAcsOG`^nPtVB6V6)lT+1a_dxp{ec1qB5cE?g)rExmd3=B-<|%FD|uDk>@~E32xi zs;jH--o0B>Q&U@8TUS?CUtizQ(9qb}*woa-;c#BOc=7V(%a)dw*0wetkJsMb{`T$L zuCA``?(X;R-}m(N^eQU$sjK((_4OkV1A2M`0|SGDgG2WA!^6WPX!OX)$mr~(BwAixURhaLU0q#UTU-D7b$xw(V{vg~V`Fo3bE~s+YinzJdwXYR=Ld+N zKX*a=+{1&6ODTJCIqmL3IC3}?lL%($k}Co4MS3SFqEQDaD!(UP`M@OoVNKRFt z6z(!Ylr}wqQJv;b(h!7^{=vb>EQS+M*puK`%{gQlTgPtN)xbPIl;c4+6w6*+Udl%M z4M>!t4a8Sh+WcAUV(&kDovBCNj4qP6O{9D%I-ye@7HyGTkkmdE;caARB!RvZ>JhI@%LyrE6IUs&Vw%mb_)cs4U zc>%J&xK{^hnHoE5pbDW(BnUoP_wMc{Lv;{+t2K7VlT~2bwbl5CDw6kx@Wl27m~f5v znjb^AF5o;zTrc_7KXZ0~n^h{FWitUwlp#U!ZBh5~Q4Gi01o`WOr)Cp3Ds>Zl=}hhsI~Fba2ON%Lo~g@hm+(9TJ=wNjM)Km z5%()BJ!5>tU%9JZ2?#FJeYfnPyv!|Jc$W29GrofmR1`S!@g?u@PKK;|om0oBu-i9l zAG~pSbsC-G>i1nr6MFbSoO$=viruB2<~1RDjGHcyy{=@K(y2ET_6TR#_d`#pz%EA_ zRfX*~%QL~4YOsjDEaw;|lM&4VoZ}fGECwBw6wV4mxww)k)N}G`ph$mDL`8 diff --git a/app/res/icon/android/Icon-xhdpi.png b/app/res/icon/android/Icon-xhdpi.png deleted file mode 100644 index 732ebb0be803d4b871711e52c7466292ce07b840..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2472 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 ZzJdPH|6hRR+csbc06fMPU28^4`WLK`R9FB2 diff --git a/app/res/icon/android/Icon-xxhdpi.png b/app/res/icon/android/Icon-xxhdpi.png deleted file mode 100644 index 3d89622af52657e3de922758a32273f7f0e6327f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4415 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 JQKsV(@qgA}7ykeN diff --git a/app/res/icon/android/Icon-xxxhdpi.png b/app/res/icon/android/Icon-xxxhdpi.png deleted file mode 100644 index 16a04c9ed3ebdf7d661bed94e377454b9e33e41c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6004 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 AW&i*H diff --git a/app/res/icon/android/hdpi.png b/app/res/icon/android/hdpi.png index 4bb37aec12f81f3f384efb07cfa4b5f186c70f3b..c213201d22b3b068cf1d35c3a082d7cda922e7e6 100644 GIT binary patch delta 1853 zcmZ{kXFS`B8pr=eY(l6NBZy5)?8reRsRU`QV+5@dTBUVLY;JHOMvd4$Zmm*!dhBtu zYL-&%L50>aZc&`7y;HQ@oLBeN{l0m|=lQ<-eV$g;1TaGe%LM>{uK^~UUAS+fiD&?5 z&KCTB3&fsbet0J$0K}*PKmrW_PT8x3H2?_L1AuiO06-Q4fYdLKIvr6tN=j62h#8ji zLKgry-5u;*aO_(S4h~LEPA)DkZf zCnqOoXJ;1|7gtwTH#avA4-XQFhtr=1m_TA75XpFPTjC_xBG72nY%a3Jwmw zfB$|+NJwaCXjoVng+d7r508k5h>D7ej*gCriHVDgi;s^_NJvOZN}|zdbUHmXH8m|Q zEj>LwBO@a-GczkIi@{*z=jRs`6ciN|6&DwmmX?;6mseC&R9042RaMp0)YR72*45S3 z*Vi{RG*BBG8=IP%nwy(jTU*=O+S=ROpFe;8;>C;3&d!%FUv_nMb$567^z`)h_V)Mp z4-O8#e*JoAXlQtNcw}Vc-Me?AqoZSEW8+Zh`1tsH1%(M1Y+_=9sjA9kGAAb|r}Xuw zrlvl8_%Ll@F+Dx~m$vp_hK4gJ)Qq+D41q8^J3B|6o134X|Mcn8!otGh;^NZM((>~1 z%F4>>>gw9s+Inj0`uh6D#>VF6=1yzt&d$#6?(SY^=l=fw!NI{HlX-Y}c(k;1baZrl ze9YR~Vr_4;SgezilT$Vq7iVlPF3#Cp{KKn1cLM&&m#>`-*3!aHkDXmE>IMOec5xrr z`uH_c++TucSDj#xS515a&m-r;N_pG0?*2UGR2_<)5cr&~QIU|Ilq5JMFCmz$^;`NI zUriY2Ti<$4kQ9kgkGd#SB$2H#zv0IBFx$gsk^lNq7QNHOO1f~W5GtrdxX{m4-Vi-p znJ-8hJbgV40Te#H(i{wx=dPrp6Sb~nGvz0QM*8Wm%+oI$A+l~~_(_kzvLz`0id{z_ zl?yPC&HR?Q9$BqfBw3L8x&?zVn127weDO3SNzV`?v7RSIMn$8>nvXI6#F zKf4TG%D#eTO3!JBP-exL3l+18xjIvSZcZl6J=6qC$TWHq5x2ITAH-!DhMBhiVcDV8 zWt|(-7Dj0@q|#WR+$dbpkUb;nS^%BQL_HmDS{Jux47n60d94u{gdj#$nGe0HVS=Vd z(3w86iZvHqpG$Fa(0W4PNmGBAVW2)nC<%U_ESYGJEcx|(nf0H?ge8c>2l-G4+%>jT zWzKl#n|F*n94QFXE-TEDTT^*-E~*M!z6Fwp^hQx1<#|SKokt9t#S25qBN7MaUM179 z2bE3^i~RjhHVz~y!OLYoy)-Fzkiyq(g3?%HH)s|XBmsdjhNYcUE$aOqabatweJevG z2^(gDUyxIZ=UBh53&N?rZ6U<%=zje|gmx(8kkGzgOkm4KCCJ@o;$2b`cm7k?d$d95 z4eHwWJwNqvyDN#b!bG^hv;8VR^H<-{m#%%aC;>C;pz}7J`nKjZL$HzJs-6e#-8>N~ zL%ep8$MkZmfMzPg8TcCF@>XpG2L8RCHJx3J%sv^C+s9XOdqai5=g3jg?*9ClH!L*VoZN{3`r79A%e?7t9b~jH z7tpmU4w4gn7XH!M>|Y?0LxcyZQgH4R@-2!#(l6AXT>ylhzKO0rLKk7?f-pep=^>3w oboGqbjfn#xg>C&$xEDeW42b^!!d2ltbG85waM!R6);>x91py&QBme*a delta 4741 zcmV;05_;``53?nZ83+OZ007#LBoL8ZAqN0CGNP$-5R>f&D1QcGOGiWi=K$vb=katA zP5=NB8%ab#RA}CvTML*?)gE7KpL6DEFqpv|33pIm4|K`BE;p68>(!_*DFNV zr<==~%^Z`^Wy_YS;|deXVjFC *K8o90(SYyphWFrhDmmyR2gn@d_N(XPRo*Otwh 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 Rxt9C@;9%!Mc#IFq_!oM#k&6HT delta 2345 zcmV+^3D)-Z3+WP&83+OZ007uvZqSikAqN2hFmczHGm~QlD1QcGOGiWi=K$vb=katA zP5=N1(Md!>R9M56S!qlZNfhqto*C{%G{kEWWie*`qgkTseMDjuH4;Tc#aoVe0V2u@ zsNe;ao*`-r!sxVTs|D!(Cu zZla`2b-s5k?|vjMq4fKU>Sub{-GXSpQWqpeMMdmpL=-sU>vq*?DDOqXB&)^@iCP=z zmUIey^5jVhbKFS`=B(|wy3z&}WZf9k(e~2re*E}xzJGHt`f+69-Lp={tn3`+FLLbI zF?NP)o^V#M@6uwsMg6O^5}~(o@}~4Xp9`n&lfrF ztRn(mSbq~LTsdLS^-Id1+h0}iGo982A-COb_mF@U3lp+u&mO%K=&~k$ z?%TI7Dk^Hyq)Dm|QH1H`<<;2KglOHpdv{n^m0w$=R*t%_*zh$PUZx0O}#w{>#L9v?4 zH32^aXi-sDb`E7{WfPGm72KIEmg3@~c~SF6{4_$6Rk3(kUcP+!@ZrN}&z^nx@+CMp zcILTYb((8+H47lNtKlqZY=d7Hh+Kq{OamzxS&B|BfEC((q<{;7hq`kx1r@( z%hIJw)o(9WRnz4)5Q;7d%{viHU#;9|Enkw6ubP0v{hA?V24O9oMd1yL|a_M1RCD zE8|!GJbJXqDJbRO+p%MZd&uRfEiElio;-goi;3uR1DP!J}6`0(M@ zty`;CuU4gLYHHfMcP|RB&6_tvCe)~{t*u2R#Xa3NP8tE=>?pJuH*VbU;lpu{U`F`h zr?Eg05fLdVDR%iUz=&EV`<0s_I4U^NT^ z-62DUOrAU$_{T1Zsi>%cK;W(T_;@(@=gv-wi^#ULG@_>;I1up9$jJEi?SC6qym|BH zy?ggqX+VyDg@@ban?0?q|8#V~7T!MI2+3{Rw!!Cq2n@8C&H4HHc=YndxELE7d*Q+b z2n6cN%F4>k%QHz5&Dg+`D$gJg?46#Tt_TDb8yXr|fh>gl86J)sQ4k8++U{7bM-CtE z*M9)$cIVEW;M>5!K$F>2R)1QyXwjm&na`~lv^jT?a?wxDzN)~#DL zEarG+aWGJEYHEtj=HR_Yj~-pWew}Nwjlsdw0|J`6x^}m=;<3EE+-B>>3dzaIP)T9r zr%#{Wx$}2M#tuwazI-|6`1$*FSUZ3aJVB;bHNa#+;Q5rBOOmOkrhjJo^yvc!4Z;|{ zL5U*tbqyUlG%q*L*3*sqq@-kQ26VBUkGFSLcJ|!4bEi$4283WTo6UwufmtWPduo0K9D2!|CAH&;#1w5#gpJb5GKY961u6i^)Lc^y$;9R;}_! zexR7zWQt$8^4z&|2r&0u)3l&2CUXka0^ey0=t4{pdP{=c|$qL zu(d7dpscLS-{0SJwN6N4LIUB{3P=KABY2o2cgjO>rn|Lw>woIql#N&G*RKx`4_B5o zNn&#88e1ko8rP=h+jmRNFVMsbq1t%ekXC-9-pjM1nRP7%R)m#?V4&@ zn-76gS63HQpMN=X=EMmTK$YB<4KDaA%1a<*(5WD(h&4xN_9*8N^U|eDpt7k`r~Vic zq89lu0YpJ6y?_7yQe3mAy@-w{PFx zzJ0sBy&ar8aNt0cFYO=OU%hJW?X^cnMvnh=eD}9*{eSUG6g}W%)E9u{;17Ob!gB(U zI3C}E%H>xSi#)vbbiDAo3 zrVm~tm6eI0deR+7WK;FgBxK{3@n^#L0No9vnz}CAk?81X{%5mMY0wDzRU};xe!+qT-+%MB0Upy3)mJ^q#8Mj3boq5XNfjp? zZ#hjJUSp4FwEq5tAh*u>OvDXU5)~CC@<`7`wF28k)5fr#GnuA^$M%%_OhFUMoH=uh z{;orJ!d-18L!78A!S9`OLRF5Z*|TRGH3_*%ij73APjDNcp3O})QvU<<&Z5dGlVR)t P015yANkvXXu0mjfljo6t diff --git a/app/res/icon/android/xhdpi.png b/app/res/icon/android/xhdpi.png index 701cad687da74c9e0cc9d15576791546c3531012..9e35d81e76ade06fb3e3007933795234b803bddc 100644 GIT binary patch delta 2406 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 KWVwz@)c*mlgBJ(@ literal 12672 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 diff --git a/app/res/icon/android/xxxhdpi.png b/app/res/icon/android/xxxhdpi.png index 6307a727e499a24e4a67957e02126c248ef0a49b..1872942c80a31a109142e99788e154dfdacf12ac 100644 GIT binary patch literal 6004 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 AZvX%Q literal 20457 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 z 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 D#<F!PNHv6FDO(>&)T5(bE2+p(6|7pvD)TC8 ztLlSxvYjezM>UkQO~w0BsiBQ(6fL8gX0iYF$G&s!x%Yne`|e-&7P5l;;ZO_|001~6 zz&BKrKi>|B_BLZ>@1;pPF*GI(0Dis%`*=cE)8n}Tp-cccO8|iEJOKEhp|TYK@C^w7 zULOYlw{ie5O{#7S*qDVX7z}1$U;u~15eS5#p`nqH(T*KE zcJAD1Y;24~B6scDWnyAtYHEr?q0neF27@s(Gs9xBI2_L0+}y�*}XAT3T9JSy@|K z+t}FH+S(Eb1UoxBdwcucyLUS{I1q`%J$v?$NF*mGCo-Ar?Ck97>Pn$ds8s5K0|z`k zJ!v!=olf`j^J6d=2M-<$3=Cv4nL$B8!NI{S7AqtqBs4Vi@ZrN@VPWCn;SmuLY&JVG zGBPSEio@Y>xm+HP7ZVc`7Z(>FAAjo9sf2`t#Kgp;q@hyLYd( zwY9CS?ZJZwot>RsU0qVCw5O-%>C>mZy}f;Xef_4U{b+Q5fB!Qqc3@yYMj*&!vcbW@ zp`oGS;o;HI(Xp|yaSCOe!5AMOpO~1KWU(eEC#R;SooIrnHcPirj*8rfc&G4m#1@tJzuc8K7$ZmPV zHOrsGgai)s;!&hd^4_7Ihq~YbuDy*xA#EO)f%{asnFw1gi;Ms~FFnYGI|g6()QO7r~rxwSPW{>Gdtn&Xfc^J2n(Ps+Y-Z`CzH8XX=PpIVhzWjchw{?p0{`YEaX(WiA0eoEkF5hO=tXRhK1?BUpQ?0w~ZuwjFMjQth>gNa+P9-8KCu;>w?QL6q@zSBa!m9(D zy6>x=|1DBcjW*;$hwWRn$o@^sskmf;(jsoq{02IW&Q9|=osbZnmzvp7wLH&rUjMw$d6F&7joH9_U;qHvOic`}sWtp>GJ&Ws z`A3bwW%ye9`w{%#M8vu3;-e}0U+)!0Q^H;#jOB9C=>uF?f?M80|0MO z!5b?rD!?3Mf(iiu7S6v(1LPH)r!E;Wre<))c~&7hMi`~TBoP2;=Pa+C#4Z(v|xXlMwB!;OrLjE#*=OiWBoO%VtL z5{bNe^{SbfnYp>Sg@uKsrKOdXm9@3Cjg5`1t*xD%oxQ!iqoX4Vg>rIoa&~sUcI}#r zi;JtPtDBpfySw}K>(@O!J<({ix3~9=8#jD>e0+U<{rvoH-MSSJ5D*v`c>DJ4;NV~k z1``$*79JiR5fKp?85tE7g~ei{qoZSDV&dcD6A}{c-o2ZYlyv|8{Ra;oBqt|7eE9Iu zqem$zDUTmNe)8l=YHDg)T3UK~dPYV@W@ctqR#r|<4i1OIEp2UW zZ{NOcZ*TAD=;-Y1?C$RF>FMe1?S1#|U0+|{`}gnr`};q9`0(-L$AN)?!NI{#pFRx@ z4Gj+ukBp3riinJkj*f|ljERblUA#CZESq1`;x!^($doM^70B6yAl(#va+(ex=P8+q);e7Gctbu{JBbYDw?<%Qf?uM6gOO%)I~{n z#eCf-%V|Kuvky)$YJ-42M6~8w`Jf-%%#zMPk`&@!El41T+7i*?2)-qH$SZ5Ttz*WW z-hz6S-lEnH@3r0W?IX<6r){nEZS2$a9|tkQuTCt?6o~X~?!3PeTnH>--Tyxvxzgp= z=2$`nlJnOk)t#gw#0p(zJdGJ74$iHETg)B~w`^HeHpP@Lln0Y_}W^9Vo7>OP$=2H@XQWkjXkzWx`{9B&g*9x@Ko1e zZvtOXeRUkaGnis&B6Y&4v$&6XZ7cJ+B~G^j98^Em(+B3tw08#;6u;c=EOKQi2aFpT zN-C<#66kjQ@vof{rWZSuu)e(f3Iu8)mWy`vbhR$7?Y zwvOycDnIL3AMzbHx#G|2!-AeJixd^AW>%ltm*n-a94eWgynN}PZlN`DJA>UQY(MV<4W{!b$x-X?j#Ipmk`btl6bxOfnh zc^p_~iZL@KxNA1G-l3#1s}SD3<8|*)D$b*gf5ZoBqcmM`eGk&t zs)b+0Z*(~#YZgPJ9G~x13f|Y!Y`OL$PA#Nq*IrkFX6Zr7@~HjruGjnX+DH8lzQZ`o z5^`>u5?+H+1`X$5ZZ>iLDvTb{-Wd%!oTa&?TWp8zKOs7!UTTeeer}xWpGDUX;usE3 z-AI2@fnDn`7K%MeKXe5V-Q!1Y*epRvOoj1S+-vx?nhf#>Cc=XCF{{*X=RdkR=A{#< zGp9ToYNV1=+a<`B{)d%Fo@=pUnux?+&+rLr-{_CbM*}mf7+9hC+%Y)Lqs01k)~Vy3 z7emsU2<`1GdbP(t=9H1~i8F~)FSE_b%fiEP!ZAW3SthTnU+Xn%hqer!{FWo?#sm$B zkw>leqs0*8oYv>DAIW|v9E8p^Tq4T5+AZ$WuF#q^w`=Q(Z=Ti6I=t9_F~+#6=H!bCK=_>dCIoS! zr1*(x5xL5I3T3m$Z*SBrPC5}1-Q4b;NXClh4hg0Nb5AMnK6eSqE!7($z>6<;s~UBW zyKM*r_?A^iPLth0vAPEA*(qsP2IC{(;C{j9meTg>2X2Z1!Ba#quy|-vGR?cc0h5i;d;z^E3e$aNf%9A)zfS# zjknFvs_}mNwc&02w1SWwA;oqus`RCW@8P?I_3G0;y}7U1%Yuu-!46QYn!op?-#S5x zBlhVx`8WUN0ROgwb(f7(K=jHp=qHsL52|5Zz`z|b27&(m=l~305RCS~puI%y_+V~| z7@L||I>fW{Qb`hjNnTV4=J|gNKpCo}CaRdu3BqXGy zq-11dH*ellP*6}* zR8&$@QdU-0QBi@xV5+LBw{G21Q&Uq{SJ%+c(A3n_($dn_*4EL{(bd(})6>(}*N4O5 zw{PDzFfcGQG&C_WF*P+cGcz+cH@C2`u(Y(ava+(awzjdcv9-0ev$M0ew|8)GaCCHZ za&o$J=Z>?pvx|$1o12@vySs;nho`3}0)g=M_V)4d@%8n+ckiCRzkg6rP;hWC5{V28 z3kweqkBEqfjEsEn;6ZeBbWBW4Y;0^?T-=ilhJ5&N=nMpr%#_f zd-nYK^A|5(q^72(rKP2(r@wspG9x1cgTZ8GW@cq&WoKvS0t;OT(_7JzIAqXc6D|2 z^z`)h_V)Gl_4oG=3=9ko4h{_s4GRbi3knVk3x5|D9vK-K9UUDT8zU$v5MVF@fiONk zJ|QkXF)=ZzpfIVVGzo)Es;f`J;gge-Q-+39Q&ZE^(=#(Ov$L~vb8`y|3qOAR`1$ka z;^N}c($X>lvAn#zva+%o6}7s$y0*5qzP`S(v9YPW69`$1D-N{}d)E~1Akg}&fXF8p%CU-ZK zyTO!08P@?75Hb^tok(YQCykU5_c8qqev@b}_Z?P(U=m&+nshNVRY9X1?h-M}e;>@P z)?=}g$2)ze2dme%xkO(TUc<*)4}t78BoGwk|7UWNOM^{Hi1yFy-KOn?&Xcbp)wP9? zYl+XQ-BonCIhhz~im9N;nH!kvHelP231>B4%dryiwqIEJ@`n1Qej7IvC6c>rh{gxW z?<0mZM09j#GdgEly!SEv4)-_OesKz?aa#_FrG`X>Af-c{BODED+~{~h2e!OIoDcFP z(l}v5#!9}bKy+>EDl4Oj9Jbp-Hrr5nr9Ls-WMhh??WFdLl73u^%h%M5RWnL(A+sROHkhCDVd%G)- z$B5*uM|2Sx=85_|byBv?)M7nAD@g+N68Vc#GqYJkooOs2Rh4jtR+IBm@ZzcH>5;ZO zR6F?a_ZGngeH1&HyPr|d;0i;Y!4Vrm0wXAYX5V)^L*Ch>ed+p1iONf%QL)3wpmZo* zmcV+vR<2H7DDDRg zVi(x-8s|aBWJ4?dV5DPy?8ObIC!J2R$eo?`z{qv6vv!pL1Q$&Bcf4kAXj?=32&wGP z!LMn+oLUp4xi*EgB3)NAih<;za6iR6jx))UrR*OR1kF;Cgm5luNwn)@LGwZ;S zKU2YH1^e-KH*aqIV%#}eP!uXE9RuPNVChY2qw#!<(FUkTMUD`d<=v{`z7s#91^gy{ z>Pl-3ImRFxso>41UMyxoh44~=jJhbre$aahjxIl;+PuYtkj{uH+4`x}$eabM&@uct zyEn8)4Q6_6;2g(sEQ%Y49jn63|5|&a4a37PJh@_y%WsS63a_bCDt_|}AF+@TF-+w6|QSDo9c}XVJyI z--kgTa|6n#ky-LxF;b89GDraYX{c8=Vk^XxQvu)(LZ`F_~I1aVM+8O zX1_WOg|CmY1|Cy6yfGf=y;uwcC30cjIa20{oy|1NpMdWLe({M7%cK9rW!N?wC(US3y*iA-1~SF;=3hPN=18u=a_?tOq8nW%ibe7kSfhU@)K8x~;0l`M|7@?!&4M z436d`D69B%PHmKYg{>g^NauV+{c z^QDYygKDimFSjR}VAiPY^4^%a!%tn=7ABV=^#?l&V1r3yup3?O8ChzMzrSsay|-zt z{@F3}sD*G=*i1StUOhdkziO;6pQJi#A z#cz6qTLr*AS&*Dy$M=USThmp;S_U5A9;ffSR^Xn{d%@Q-AOlup2Z}AFh@2=xinYx7 zKCRZ{;G+pix5Ck&u8_d^I-vty1d#wlgK#*?#)))-Rt!+qaK-yPw1j~CPRY$$!8@B{dOnKAHj1g_e&?0b76~9Us8zr~( zLFl$(W!neMKGLF1zluuhT1UcN>`zm`agx&a2 zFjIHL34YEi8Y+*9l8RVtp!%Rgluq(N-?y% zV`i8fw0sS~dK-J2x;38Sdn+5(YG>JohYsQ16h_)SdiQM$_%0tq)K*d?i*Fi1+O@eQ zM#f3^lYRK4Wv(wk(G|y~IBWmBCMz1eAVtUz?22fKf3!`u_fSV3bzbRlKQ-%HI)Q@p~89JlMppd6y{uQB#D`H(>vi&Lo@L9Nu-$#js?0 zG$!5~v|D?g>~WV{R8oz+RPI4M9Nlfc&?C@MagD0-doXVmelNx0Y;}(`e-&j=vKD~h z3Nq+sK&`?u+zZJdB#aCEkw#d$a*g zViV8yl$fk2pq!cZOi`zqxyB)6ExG&Aw3N;ETzMfrbT#&A{(4!dury%_uI7w6eb?0g zrO@b_B*UcXsfR-40Q z0-&7K<=)t7It)Rfa&6 pb#(yzfT*?*d)A-zt{^0Ip-KU7_g``yadv)4~7% diff --git a/app/res/icon/ios/icon-60.png b/app/res/icon/ios/icon-60.png index e537029ebf85af096e07adf4ea6417ac8f5c2404..2fd6325bc690d5c0fb407e97c32a6ec98c9a57ce 100644 GIT binary patch delta 1570 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 delta 9314 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 Qr2qf`07*qoM6N<$f;uScm;e9( diff --git a/app/res/icon/ios/icon-60@3x.png b/app/res/icon/ios/icon-60@3x.png index 9091c339f257aee4dcf81f3c6c367aec68d47663..16204bde8a08ec62f5eea22453bc3af0c113ac3b 100644 GIT binary patch literal 3700 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 literal 17617 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 From c324910a1d190126d322039a1c41d5d9655867a2 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 23:10:18 -0500 Subject: [PATCH 313/414] Consistent naming, new icon, building for more testers --- app/config.xml | 2 +- app/package.json | 2 +- app/www/index.html | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/config.xml b/app/config.xml index f88c33b..083cdde 100644 --- a/app/config.xml +++ b/app/config.xml @@ -1,5 +1,5 @@ - + INTVAL3 Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras diff --git a/app/package.json b/app/package.json index 8d5aace..7120069 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "com.sixteenmillimeter.intval3", "displayName": "INTVAL3", - "version": "1.0.1", + "version": "1.0.2", "description": "Mobile control app for the INTVAL intervalometer for Bolex 16mm cameras", "main": "index.js", "scripts": { diff --git a/app/www/index.html b/app/www/index.html index 4bc3fce..0e6fe49 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -1,7 +1,7 @@ - INTVAL + INTVAL3 @@ -14,7 +14,7 @@

    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 Or20tfVc`R_cmD?u=Egw) literal 0 HcmV?d00001 From a26d73bece56880a9bef9181274cae9dcd0cc7ab Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 29 Dec 2017 22:42:49 -0500 Subject: [PATCH 309/414] Fix intval button text changes --- app/www/static/js/intval.core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 6663b14..a59a816 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -117,11 +117,11 @@ var setDirLabel = function (dir) { if (dir) { bwdLabel.classList.remove('selected'); fwdLabel.classList.add('selected'); - frame.innerHTML = '+1 FRAME'; + but.innerHTML = '+1 FRAME'; } else { fwdLabel.classList.remove('selected'); bwdLabel.classList.add('selected'); - frame.innerHTML = '-1 FRAME'; + but.innerHTML = '-1 FRAME'; } }; var incCounter = function (val) { From c599e3480b2e6fc9aca90730b6d20890cce4e2ce Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 29 Dec 2017 23:08:39 -0500 Subject: [PATCH 310/414] optimize images after resizing --- app/hooks/icons.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/hooks/icons.sh b/app/hooks/icons.sh index d63e9a1..8ed9976 100644 --- a/app/hooks/icons.sh +++ b/app/hooks/icons.sh @@ -23,4 +23,9 @@ convert "$base" -resize '48x48' -unsharp 1x4 "res/icon/android/Icon-mdpi.png convert "$base" -resize '72x72' -unsharp 1x4 "res/icon/android/Icon-hdpi.png" convert "$base" -resize '96x96' -unsharp 1x4 "res/icon/android/Icon-xhdpi.png" convert "$base" -resize '144x144' -unsharp 1x4 "res/icon/android/Icon-xxhdpi.png" -convert "$base" -resize '192x192' -unsharp 1x4 "res/icon/android/Icon-xxxhdpi.png" \ No newline at end of file +convert "$base" -resize '192x192' -unsharp 1x4 "res/icon/android/Icon-xxxhdpi.png" + +cd res/icon/ios/ +find -type f -name "*.png" -exec optipng -o7 {} \; +cd ../android/ +find -type f -name "*.png" -exec optipng -o7 {} \; \ No newline at end of file From 2398285006af99fee6d2710c838acf5c2d6fb83a Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 29 Dec 2017 23:09:07 -0500 Subject: [PATCH 311/414] Re-created and optimized all images. --- app/res/icon/android/Icon-hdpi.png | Bin 0 -> 1920 bytes app/res/icon/android/Icon-mdpi.png | Bin 0 -> 1526 bytes app/res/icon/android/Icon-xhdpi.png | Bin 0 -> 2472 bytes app/res/icon/android/Icon-xxhdpi.png | Bin 0 -> 4415 bytes app/res/icon/android/Icon-xxxhdpi.png | Bin 0 -> 6004 bytes app/res/icon/android/hdpi.png | Bin 6342 -> 4787 bytes app/res/icon/android/mdpi.png | Bin 3128 -> 2409 bytes app/res/icon/android/xhdpi.png | Bin 8667 -> 6417 bytes app/res/icon/android/xxhdpi.png | Bin 17345 -> 12672 bytes app/res/icon/android/xxxhdpi.png | Bin 26986 -> 20457 bytes app/res/icon/ios/Icon-60.png | Bin 0 -> 1643 bytes app/res/icon/ios/Icon-60@2x.png | Bin 0 -> 2740 bytes app/res/icon/ios/Icon-60@3x.png | Bin 0 -> 3700 bytes app/res/icon/ios/icon-60.png | Bin 5157 -> 3832 bytes app/res/icon/ios/icon-60@2x.png | Bin 12656 -> 9324 bytes app/res/icon/ios/icon-60@3x.png | Bin 23504 -> 17617 bytes 16 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/res/icon/android/Icon-hdpi.png create mode 100644 app/res/icon/android/Icon-mdpi.png create mode 100644 app/res/icon/android/Icon-xhdpi.png create mode 100644 app/res/icon/android/Icon-xxhdpi.png create mode 100644 app/res/icon/android/Icon-xxxhdpi.png create mode 100644 app/res/icon/ios/Icon-60.png create mode 100644 app/res/icon/ios/Icon-60@2x.png create mode 100644 app/res/icon/ios/Icon-60@3x.png diff --git a/app/res/icon/android/Icon-hdpi.png b/app/res/icon/android/Icon-hdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..235239e893a0bd2381f6844c8b9177555e7035ff GIT binary patch literal 1920 zcmZ`(X*ApW7X2Gyh#@u96hja*EvYeb5lJdR+K5~#Xq8Y(r6p#4MI(k9LQTsvmeSj6 zEUs3KrF6Kc(7MGdN^do0rKxJU>HGFRyuH>r=XcKD``cc>4AL1VDXDqe}@I&$wUB%*9CwiCIEcnpORJpAj%W~xPAbD zECB$;t7TnoC_W*674H=Z01|RPO8_V>Q{o>*83ZCubWu`WSQN?a#oq(~!E+>63XV?+ z2nYxY3JM7c2@4B@Kp+tj5m8Z5F)=Z5ad9vhEFmEwDJdx>B_%B_4S_(89Xlo?BO@y- zD<>x>FE4-m_;CdV1w}VlWsi7K_8-oSdBSc)YW-vx|$1tE(%KNIZM?tecw~nM`(fcc)M&9v&W^ zo}OM_UQ{a8+uPg6$LGR@3x0ln{{H?n8Z9s|FeoS}G&D3UEG#@coKB}lL_|bJMlu+T zsHmvu=;+wk*tod3`1tt5#Kh~@uO}rXrKF@VnM@Xom7boSk&%&^nVFT9m7SfPlas?{ zvkM9e3JVL1i;GK2O3KU2D=RCjs;a81t7~d%>gww1>+2gD8X6lLo0^)Mo10r&T3TCM z+uGXN+uJ)jIvzfJ_~_B2uCA`fj~{n;clY%4^!E1l_4N%53=9noJ$?FgczAeZWaRnt z=PzHr9334U8yg#kLdVC)U#Y84z+e*-6C52K4u>;2IXPu!HZ?W%`t|E+JG<%W>A#GO z{xUazi$cA1bbL!7%*@Qp&d$!w&Aof~Zhn4#VPRo$adByBX?c11!-o$mD=XaebS{^> zy1Kfywzk>Uwz;{vwY9a~)wQ#;^ZE1VT@Gh=cXw}bac^($%a<>_^>yCH29L+v-{1en z$I;ONA4f-rd>sA6lRr0uerC(x#R+R~7huW{wa_X7i>90#Tz~T^MlMiZWJ`~5>UJ&h z8T>XaA672fVR-r1DRONDb^`o9Q@<)HGbKf0N=;rO)$q5>XZ{8-!599Gf*?gIyAgF% zq(P-QVt&JoZ(}w_Y-0ZPu_A7>o0oD#u97UQMo?IX+Runy9-K#1eUbj&rXYrxX?Y$- zI=nX4$!-?O_4?{Oj3cU|~NjKyDI<{8C%WO!Xh-_c+17myU+*X0Uf35Wmy zaFiy9?opGoc#SzZn$`Eq-VADm3(sW_fJ3;b5Ei^lv+9rNScBN;8`Odcpd7FRs% z%8oq~#G-Lf_eNT{a<1%QN>Pf>3Xx3+W!F^rv1*zon5G2d={<)8+hg3>G&0HXE9$-*&8LNvB{Vt?=`zn%4!m;U*xQ?u!S6 z#7&b=ABoUT^*k!t|DzRb$)Xgw)k?ffZ4oZG=W&HO1Ux)#*0Yld_GG1%88 z#0$22KvQ|^dE+)*?R@ZnW_Gtn!ljpmF#l?G9BWzMFYL!umst`Hp=5t>34REv6Ppa? zgbm?29ge517xNhl&ii+g(|Z!qSS3TU7EXD)Sw&z}(?)|^fr7rgpl8Vs1C;)#!~i|e zcbb@*{Ov?fDDzg^n$K7-M>cQp-UrUiFbcrZy|tp=d7+vKoT#)`4KMuI>i)s5=bI<0 z+rve``qhHEDUByyA literal 0 HcmV?d00001 diff --git a/app/res/icon/android/Icon-mdpi.png b/app/res/icon/android/Icon-mdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..d7912c8b20b0438a6679630dc7f8d32c0d5d0fe2 GIT binary patch literal 1526 zcmZ`%c{tMz9RD$F=FE|6&B$%uX6~Whv1Mqi!c5L=<(hLIkL$JMbyj+M9UTf$61tgI zXi6bMdL6kQd2=k&=H`{&pZCA_kN5LD-}CeN{P)SDc(};HHQ@jN$hndTC%}o>D;Naq z!Q;}6;E*_rC*uL&aiPpkh$MKULP;mc0FYt;0PHLP*alzhc>qWRjr?E$z+D9Zwb(N5 zaR-o)jwR6&06<20uf%}sW$NG)%5o(Wq0_QR2`G-=N;(e!;--{iUPO=*6B82`7l%L~ z5)u-Ul9EzVQcx%q27^gUOT*!C85tQ_Sy?$bIeB?`1qB5~Ma6yl_9-bTDJv_hsHmu_ zs_x&vUrkL-U0q#6Lqk(jQ%g$=fj}HUAdpC;wzf73h0@W{(bd(}*Vi{NFfcMQGB!3g zF)=YUHASP*znGa}Fc@=lb1W8XVPRouX=!C;Wo>P3V`F1$Yinm`hr{9Q?d=^L92^}T z@p!zGlasTvGl4)L5{YhZZtm{xWHQ;))00A>czJnwdwcu%_)w`-UteDujppy~9~c-I z6ciL392^o7LZ{Oi3`SU3Sa^7NL_|bnWMouS6qCt}j*gCriHVPoPe@2eOiWBlN=i;n zPEAcsOG`^nPtVB6V6)lT+1a_dxp{ec1qB5cE?g)rExmd3=B-<|%FD|uDk>@~E32xi zs;jH--o0B>Q&U@8TUS?CUtizQ(9qb}*woa-;c#BOc=7V(%a)dw*0wetkJsMb{`T$L zuCA``?(X;R-}m(N^eQU$sjK((_4OkV1A2M`0|SGDgG2WA!^6WPX!OX)$mr~(BwAixURhaLU0q#UTU-D7b$xw(V{vg~V`Fo3bE~s+YinzJdwXYR=Ld+N zKX*a=+{1&6ODTJCIqmL3IC3}?lL%($k}Co4MS3SFqEQDaD!(UP`M@OoVNKRFt z6z(!Ylr}wqQJv;b(h!7^{=vb>EQS+M*puK`%{gQlTgPtN)xbPIl;c4+6w6*+Udl%M z4M>!t4a8Sh+WcAUV(&kDovBCNj4qP6O{9D%I-ye@7HyGTkkmdE;caARB!RvZ>JhI@%LyrE6IUs&Vw%mb_)cs4U zc>%J&xK{^hnHoE5pbDW(BnUoP_wMc{Lv;{+t2K7VlT~2bwbl5CDw6kx@Wl27m~f5v znjb^AF5o;zTrc_7KXZ0~n^h{FWitUwlp#U!ZBh5~Q4Gi01o`WOr)Cp3Ds>Zl=}hhsI~Fba2ON%Lo~g@hm+(9TJ=wNjM)Km z5%()BJ!5>tU%9JZ2?#FJeYfnPyv!|Jc$W29GrofmR1`S!@g?u@PKK;|om0oBu-i9l zAG~pSbsC-G>i1nr6MFbSoO$=viruB2<~1RDjGHcyy{=@K(y2ET_6TR#_d`#pz%EA_ zRfX*~%QL~4YOsjDEaw;|lM&4VoZ}fGECwBw6wV4mxww)k)N}G`ph$mDL`8 literal 0 HcmV?d00001 diff --git a/app/res/icon/android/Icon-xhdpi.png b/app/res/icon/android/Icon-xhdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..732ebb0be803d4b871711e52c7466292ce07b840 GIT binary patch literal 2472 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 ZzJdPH|6hRR+csbc06fMPU28^4`WLK`R9FB2 literal 0 HcmV?d00001 diff --git a/app/res/icon/android/Icon-xxhdpi.png b/app/res/icon/android/Icon-xxhdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..3d89622af52657e3de922758a32273f7f0e6327f GIT binary patch literal 4415 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 JQKsV(@qgA}7ykeN literal 0 HcmV?d00001 diff --git a/app/res/icon/android/Icon-xxxhdpi.png b/app/res/icon/android/Icon-xxxhdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..16a04c9ed3ebdf7d661bed94e377454b9e33e41c GIT binary patch literal 6004 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 AW&i*H literal 0 HcmV?d00001 diff --git a/app/res/icon/android/hdpi.png b/app/res/icon/android/hdpi.png index 35368193e40dfa6438c353c5aecbca24b5dcb4a4..4bb37aec12f81f3f384efb07cfa4b5f186c70f3b 100644 GIT binary patch delta 4715 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 delta 6297 zcmV;K7-r|QCB`w385#xv0033(vqt~`00v@9M??VU0OtVb@pKVRks(ii7%oXfK~#9! ztz3PKT~)RJt$psfcRo5ZZD-(hKA?3vg<76M+o8p_mZlVns1MOsgQ*BH{-KFa68|t7 z1F7K;g9Q?OiAsV}KVodlSF{Q6n(&@L5fKgjkPg#A>CmY#U!57IcV_OrXRr6i{y2M| zk6WrsW{?D=aP9{Q!2(PK27>^QWbdJci0=&oAlV9?8c#|nGeI3C zk