Improved error handling and reporting, Check for firmware updates, Password control...
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 9 Feb 2018 03:35:00 +0000 (19:35 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 9 Feb 2018 03:35:00 +0000 (19:35 -0800)
39 files changed:
Makefile
jshint.json [new file with mode: 0644]
package-lock.json [new file with mode: 0644]
package.json
src/jade/index.jade
src/jade/templates/admin-view.jade
src/jade/templates/console.jade [new file with mode: 0644]
src/jade/templates/control-view.jade
src/js/admin-view.js
src/js/app.js
src/js/console.js [new file with mode: 0644]
src/js/control-view.js
src/js/gauge.js
src/js/io-view.js
src/js/main.js
src/js/motor-view.js
src/js/slider.js [deleted file]
src/js/tool-view.js
src/py/bbctrl/APIHandler.py
src/py/bbctrl/AVR.py
src/py/bbctrl/Cmd.py
src/py/bbctrl/Config.py
src/py/bbctrl/Ctrl.py
src/py/bbctrl/Jog.py
src/py/bbctrl/LCD.py
src/py/bbctrl/Messages.py [new file with mode: 0644]
src/py/bbctrl/Planner.py
src/py/bbctrl/Pwr.py
src/py/bbctrl/State.py
src/py/bbctrl/Web.py
src/py/bbctrl/__init__.py
src/py/inevent/Event.py
src/py/inevent/EventStream.py
src/py/inevent/FindDevices.py
src/resources/config-template.json
src/resources/css/fd-slider-tooltip.css [deleted file]
src/resources/css/fd-slider.css [deleted file]
src/resources/js/fd-slider.min.js [deleted file]
src/stylus/style.styl

index aaaf44bd92f1070464e9177267172e88e7baaddb..8b154350905b2b70a9ebf806ea6109c02bc95a91 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -136,6 +136,14 @@ build/css/%.css: src/stylus/%.styl node_modules
        mkdir -p $(shell dirname $@)
        $(STYLUS) < $< > $@ || (rm -f $@; exit 1)
 
+pylint:
+       pylint3 -E $(shell find src/py -name \*.py)
+
+jshint:
+       ./node_modules/jshint/bin/jshint --config jshint.json src/js/*.js
+
+lint: pylint jshint
+
 watch:
        @clear
        $(MAKE)
@@ -159,4 +167,4 @@ dist-clean: clean
        rm -rf node_modules
 
 .PHONY: all install html css static templates clean tidy copy mount umount pkg
-.PHONY: gplan
+.PHONY: gplan lint pylint jshint
diff --git a/jshint.json b/jshint.json
new file mode 100644 (file)
index 0000000..4a7add2
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "asi": true,
+  "browser": true,
+  "devel": true,
+  "strict": "global",
+  "globals": {
+    "$": false,
+    "require": false,
+    "module": false,
+    "Vue": false,
+    "SockJS": false,
+    "Gauge": false
+  }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..9e7bec2
--- /dev/null
@@ -0,0 +1,1794 @@
+{
+  "name": "bbctrl",
+  "version": "0.3.4",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@browserify/acorn5-object-spread": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@browserify/acorn5-object-spread/-/acorn5-object-spread-5.0.1.tgz",
+      "integrity": "sha512-sFCUPzgeEjdq3rinwy4TFXtak2YZdhqpj6MdNusxkdTFr9TXAUEYK4YQSamR8Joqt/yii1drgl5hk8q/AtJDKA==",
+      "requires": {
+        "acorn": "5.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz",
+          "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug=="
+        }
+      }
+    },
+    "JSONStream": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz",
+      "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=",
+      "requires": {
+        "jsonparse": "1.3.1",
+        "through": "2.3.8"
+      }
+    },
+    "acorn": {
+      "version": "4.0.13",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+      "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+    },
+    "acorn-globals": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz",
+      "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=",
+      "requires": {
+        "acorn": "2.7.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
+          "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc="
+        }
+      }
+    },
+    "align-text": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+      "requires": {
+        "kind-of": "3.2.2",
+        "longest": "1.0.1",
+        "repeat-string": "1.6.1"
+      }
+    },
+    "amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+    },
+    "ansi-styles": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
+      "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
+      "requires": {
+        "color-convert": "1.9.1"
+      }
+    },
+    "array-filter": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+      "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
+    },
+    "array-map": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+      "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
+    },
+    "array-reduce": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+      "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys="
+    },
+    "asap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz",
+      "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0="
+    },
+    "asn1.js": {
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz",
+      "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==",
+      "requires": {
+        "bn.js": "4.11.8",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "assert": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+      "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+      "requires": {
+        "util": "0.10.3"
+      }
+    },
+    "astw": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz",
+      "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=",
+      "requires": {
+        "acorn": "4.0.13"
+      }
+    },
+    "autoprefixer": {
+      "version": "7.2.5",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.5.tgz",
+      "integrity": "sha512-XqHfo8Ht0VU+T5P+eWEVoXza456KJ4l62BPewu3vpNf3LP9s2+zYXkXBznzYby4XeECXgG3N4i+hGvOhXErZmA==",
+      "requires": {
+        "browserslist": "2.11.1",
+        "caniuse-lite": "1.0.30000791",
+        "normalize-range": "0.1.2",
+        "num2fraction": "1.2.2",
+        "postcss": "6.0.16",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base64-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
+      "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
+    },
+    "bn.js": {
+      "version": "4.11.8",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+    },
+    "brace-expansion": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
+      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+      "requires": {
+        "balanced-match": "1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+    },
+    "browser-pack": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz",
+      "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
+      "requires": {
+        "JSONStream": "1.3.2",
+        "combine-source-map": "0.7.2",
+        "defined": "1.0.0",
+        "through2": "2.0.3",
+        "umd": "3.0.1"
+      }
+    },
+    "browser-resolve": {
+      "version": "1.11.2",
+      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz",
+      "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=",
+      "requires": {
+        "resolve": "1.1.7"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
+        }
+      }
+    },
+    "browserify": {
+      "version": "15.1.0",
+      "resolved": "https://registry.npmjs.org/browserify/-/browserify-15.1.0.tgz",
+      "integrity": "sha512-CulQKBiROFN+EssphwJEY4wWBACqWR1Iv+wxgO0GvOiweLb1Rwja4J/XbTCxx1ixTDK6llgUB3iG8fFxLBbJDA==",
+      "requires": {
+        "JSONStream": "1.3.2",
+        "assert": "1.4.1",
+        "browser-pack": "6.0.2",
+        "browser-resolve": "1.11.2",
+        "browserify-zlib": "0.2.0",
+        "buffer": "5.0.8",
+        "cached-path-relative": "1.0.1",
+        "concat-stream": "1.5.2",
+        "console-browserify": "1.1.0",
+        "constants-browserify": "1.0.0",
+        "crypto-browserify": "3.12.0",
+        "defined": "1.0.0",
+        "deps-sort": "2.0.0",
+        "domain-browser": "1.1.7",
+        "duplexer2": "0.1.4",
+        "events": "1.1.1",
+        "glob": "7.1.2",
+        "has": "1.0.1",
+        "htmlescape": "1.1.1",
+        "https-browserify": "1.0.0",
+        "inherits": "2.0.3",
+        "insert-module-globals": "7.0.1",
+        "labeled-stream-splicer": "2.0.0",
+        "module-deps": "5.0.1",
+        "os-browserify": "0.3.0",
+        "parents": "1.0.1",
+        "path-browserify": "0.0.0",
+        "process": "0.11.10",
+        "punycode": "1.4.1",
+        "querystring-es3": "0.2.1",
+        "read-only-stream": "2.0.0",
+        "readable-stream": "2.3.3",
+        "resolve": "1.5.0",
+        "shasum": "1.0.2",
+        "shell-quote": "1.6.1",
+        "stream-browserify": "2.0.1",
+        "stream-http": "2.7.2",
+        "string_decoder": "1.0.3",
+        "subarg": "1.0.0",
+        "syntax-error": "1.3.0",
+        "through2": "2.0.3",
+        "timers-browserify": "1.4.2",
+        "tty-browserify": "0.0.0",
+        "url": "0.11.0",
+        "util": "0.10.3",
+        "vm-browserify": "0.0.4",
+        "xtend": "4.0.1"
+      }
+    },
+    "browserify-aes": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
+      "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==",
+      "requires": {
+        "buffer-xor": "1.0.3",
+        "cipher-base": "1.0.4",
+        "create-hash": "1.1.3",
+        "evp_bytestokey": "1.0.3",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
+      "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
+      "requires": {
+        "browserify-aes": "1.1.1",
+        "browserify-des": "1.0.0",
+        "evp_bytestokey": "1.0.3"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
+      "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "des.js": "1.0.0",
+        "inherits": "2.0.3"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "randombytes": "2.0.6"
+      }
+    },
+    "browserify-sign": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "elliptic": "6.4.0",
+        "inherits": "2.0.3",
+        "parse-asn1": "5.1.0"
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "requires": {
+        "pako": "1.0.6"
+      }
+    },
+    "browserslist": {
+      "version": "2.11.1",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.1.tgz",
+      "integrity": "sha512-Gp4oJOQOby5TpOJJuUtCrGE0KSJOUYVa/I+/3eD/TRWEK8jqZuJPAK1t+VuG6jp0keudrqtxlH4MbYbmylun9g==",
+      "requires": {
+        "caniuse-lite": "1.0.30000791",
+        "electron-to-chromium": "1.3.30"
+      }
+    },
+    "buffer": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz",
+      "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==",
+      "requires": {
+        "base64-js": "1.2.1",
+        "ieee754": "1.1.8"
+      }
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
+    },
+    "cached-path-relative": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
+      "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc="
+    },
+    "camelcase": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+      "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+    },
+    "caniuse-lite": {
+      "version": "1.0.30000791",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000791.tgz",
+      "integrity": "sha1-jjV0Xv1IOj4ju301CZAybSMZ/BY="
+    },
+    "center-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+      "requires": {
+        "align-text": "0.1.4",
+        "lazy-cache": "1.0.4"
+      }
+    },
+    "chalk": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
+      "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
+      "requires": {
+        "ansi-styles": "3.2.0",
+        "escape-string-regexp": "1.0.5",
+        "supports-color": "4.5.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
+          "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
+          "requires": {
+            "has-flag": "2.0.0"
+          }
+        }
+      }
+    },
+    "character-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz",
+      "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY="
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "requires": {
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "clean-css": {
+      "version": "3.4.28",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz",
+      "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
+      "requires": {
+        "commander": "2.8.1",
+        "source-map": "0.4.4"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.8.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
+          "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
+          "requires": {
+            "graceful-readlink": "1.0.1"
+          }
+        },
+        "source-map": {
+          "version": "0.4.4",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+          "requires": {
+            "amdefine": "1.0.1"
+          }
+        }
+      }
+    },
+    "cli": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+      "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+      "requires": {
+        "exit": "0.1.2",
+        "glob": "7.1.2"
+      }
+    },
+    "cliui": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+      "requires": {
+        "center-align": "0.1.3",
+        "right-align": "0.1.3",
+        "wordwrap": "0.0.2"
+      },
+      "dependencies": {
+        "wordwrap": {
+          "version": "0.0.2",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
+        }
+      }
+    },
+    "color-convert": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+      "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+    },
+    "combine-source-map": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz",
+      "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=",
+      "requires": {
+        "convert-source-map": "1.1.3",
+        "inline-source-map": "0.6.2",
+        "lodash.memoize": "3.0.4",
+        "source-map": "0.5.7"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "commander": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
+      "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz",
+      "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.0.6",
+        "typedarray": "0.0.6"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+          "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "string_decoder": "0.10.31",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        }
+      }
+    },
+    "console-browserify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+      "requires": {
+        "date-now": "0.1.4"
+      }
+    },
+    "constantinople": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz",
+      "integrity": "sha1-S5RdmTeQe82Y7ldRIsOBdRZUQUE=",
+      "requires": {
+        "acorn": "2.7.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
+          "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc="
+        }
+      }
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
+    },
+    "convert-source-map": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+      "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "create-ecdh": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
+      "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "elliptic": "6.4.0"
+      }
+    },
+    "create-hash": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
+      "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "inherits": "2.0.3",
+        "ripemd160": "2.0.1",
+        "sha.js": "2.4.9"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
+      "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "create-hash": "1.1.3",
+        "inherits": "2.0.3",
+        "ripemd160": "2.0.1",
+        "safe-buffer": "5.1.1",
+        "sha.js": "2.4.9"
+      }
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "requires": {
+        "browserify-cipher": "1.0.0",
+        "browserify-sign": "4.0.4",
+        "create-ecdh": "4.0.0",
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "diffie-hellman": "5.0.2",
+        "inherits": "2.0.3",
+        "pbkdf2": "3.0.14",
+        "public-encrypt": "4.0.0",
+        "randombytes": "2.0.6",
+        "randomfill": "1.0.3"
+      }
+    },
+    "css": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz",
+      "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=",
+      "requires": {
+        "css-parse": "1.0.4",
+        "css-stringify": "1.0.5"
+      }
+    },
+    "css-parse": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz",
+      "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90="
+    },
+    "css-stringify": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz",
+      "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE="
+    },
+    "date-now": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
+    },
+    "debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+    },
+    "defined": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
+    },
+    "deps-sort": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
+      "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
+      "requires": {
+        "JSONStream": "1.3.2",
+        "shasum": "1.0.2",
+        "subarg": "1.0.0",
+        "through2": "2.0.3"
+      }
+    },
+    "des.js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+      "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+      "requires": {
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "detective": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/detective/-/detective-5.0.2.tgz",
+      "integrity": "sha512-NUsLoezj4wb9o7vpxS9F3L5vcO87ceyRBcl48op06YFNwkyIEY997JpSCA5lDlDuDc6JxOtaL5qfK3muoWxpMA==",
+      "requires": {
+        "@browserify/acorn5-object-spread": "5.0.1",
+        "acorn": "5.3.0",
+        "defined": "1.0.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz",
+          "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug=="
+        }
+      }
+    },
+    "diffie-hellman": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
+      "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "miller-rabin": "4.0.1",
+        "randombytes": "2.0.6"
+      }
+    },
+    "dom-serializer": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+      "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+      "requires": {
+        "domelementtype": "1.1.3",
+        "entities": "1.1.1"
+      },
+      "dependencies": {
+        "domelementtype": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
+        },
+        "entities": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
+          "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
+        }
+      }
+    },
+    "domain-browser": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
+      "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw="
+    },
+    "domelementtype": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
+      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
+    },
+    "domhandler": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+      "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+      "requires": {
+        "domelementtype": "1.3.0"
+      }
+    },
+    "domutils": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+      "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+      "requires": {
+        "dom-serializer": "0.1.0",
+        "domelementtype": "1.3.0"
+      }
+    },
+    "duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+      "requires": {
+        "readable-stream": "2.3.3"
+      }
+    },
+    "electron-releases": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/electron-releases/-/electron-releases-2.1.0.tgz",
+      "integrity": "sha512-cyKFD1bTE/UgULXfaueIN1k5EPFzs+FRc/rvCY5tIynefAPqopQEgjr0EzY+U3Dqrk/G4m9tXSPuZ77v6dL/Rw=="
+    },
+    "electron-to-chromium": {
+      "version": "1.3.30",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz",
+      "integrity": "sha512-zx1Prv7kYLfc4OA60FhxGbSo4qrEjgSzpo1/37i7l9ltXPYOoQBtjQxY9KmsgfHnBxHlBGXwLlsbt/gub1w5lw==",
+      "requires": {
+        "electron-releases": "2.1.0"
+      }
+    },
+    "elliptic": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
+      "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0",
+        "hash.js": "1.1.3",
+        "hmac-drbg": "1.0.1",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0",
+        "minimalistic-crypto-utils": "1.0.1"
+      }
+    },
+    "entities": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+      "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY="
+    },
+    "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="
+    },
+    "events": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+    },
+    "evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "requires": {
+        "md5.js": "1.3.4",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw="
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "glob": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+      "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-readlink": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
+    },
+    "has": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+      "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+      "requires": {
+        "function-bind": "1.1.1"
+      }
+    },
+    "has-flag": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
+    },
+    "hash-base": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
+      "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
+      "requires": {
+        "inherits": "2.0.3"
+      }
+    },
+    "hash.js": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+      "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
+      "requires": {
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "requires": {
+        "hash.js": "1.1.3",
+        "minimalistic-assert": "1.0.0",
+        "minimalistic-crypto-utils": "1.0.1"
+      }
+    },
+    "htmlescape": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+      "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
+    },
+    "htmlparser2": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+      "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+      "requires": {
+        "domelementtype": "1.3.0",
+        "domhandler": "2.3.0",
+        "domutils": "1.5.1",
+        "entities": "1.0.0",
+        "readable-stream": "1.1.14"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "readable-stream": {
+          "version": "1.1.14",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "0.0.1",
+            "string_decoder": "0.10.31"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        }
+      }
+    },
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
+    },
+    "ieee754": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
+      "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
+    },
+    "indexof": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "inline-source-map": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+      "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+      "requires": {
+        "source-map": "0.5.7"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "insert-module-globals": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz",
+      "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
+      "requires": {
+        "JSONStream": "1.3.2",
+        "combine-source-map": "0.7.2",
+        "concat-stream": "1.5.2",
+        "is-buffer": "1.1.6",
+        "lexical-scope": "1.2.0",
+        "process": "0.11.10",
+        "through2": "2.0.3",
+        "xtend": "4.0.1"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "jade": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz",
+      "integrity": "sha1-nIDlOMEtP7lcjZu5VZ+gzAQEBf0=",
+      "requires": {
+        "character-parser": "1.2.1",
+        "clean-css": "3.4.28",
+        "commander": "2.6.0",
+        "constantinople": "3.0.2",
+        "jstransformer": "0.0.2",
+        "mkdirp": "0.5.1",
+        "transformers": "2.1.0",
+        "uglify-js": "2.8.29",
+        "void-elements": "2.0.1",
+        "with": "4.0.3"
+      }
+    },
+    "jshint": {
+      "version": "2.9.5",
+      "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz",
+      "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=",
+      "requires": {
+        "cli": "1.0.1",
+        "console-browserify": "1.1.0",
+        "exit": "0.1.2",
+        "htmlparser2": "3.8.3",
+        "lodash": "3.7.0",
+        "minimatch": "3.0.4",
+        "shelljs": "0.3.0",
+        "strip-json-comments": "1.0.4"
+      }
+    },
+    "json-stable-stringify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+      "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+      "requires": {
+        "jsonify": "0.0.0"
+      }
+    },
+    "jsonify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
+    },
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+    },
+    "jstransformer": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz",
+      "integrity": "sha1-eq4pqQPRls+glz2IXT5HlH7Ndqs=",
+      "requires": {
+        "is-promise": "2.1.0",
+        "promise": "6.1.0"
+      }
+    },
+    "kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "requires": {
+        "is-buffer": "1.1.6"
+      }
+    },
+    "labeled-stream-splicer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz",
+      "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=",
+      "requires": {
+        "inherits": "2.0.3",
+        "isarray": "0.0.1",
+        "stream-splicer": "2.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        }
+      }
+    },
+    "lazy-cache": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
+    },
+    "lexical-scope": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz",
+      "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=",
+      "requires": {
+        "astw": "2.2.0"
+      }
+    },
+    "lodash": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz",
+      "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU="
+    },
+    "lodash.memoize": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+      "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8="
+    },
+    "longest": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+    },
+    "md5.js": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
+      "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
+      "requires": {
+        "hash-base": "3.0.4",
+        "inherits": "2.0.3"
+      },
+      "dependencies": {
+        "hash-base": {
+          "version": "3.0.4",
+          "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+          "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+          "requires": {
+            "inherits": "2.0.3",
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "requires": {
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0"
+      }
+    },
+    "minimalistic-assert": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
+      "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "1.1.8"
+      }
+    },
+    "minimist": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+        }
+      }
+    },
+    "module-deps": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-5.0.1.tgz",
+      "integrity": "sha512-sigq/hm/L+Z5IGi1DDl0x2ptkw7S86aFh213QhPLD8v9Opv90IHzKIuWJrRa5bJ77DVKHco2CfIEuThcT/vDJA==",
+      "requires": {
+        "JSONStream": "1.3.2",
+        "browser-resolve": "1.11.2",
+        "cached-path-relative": "1.0.1",
+        "concat-stream": "1.6.0",
+        "defined": "1.0.0",
+        "detective": "5.0.2",
+        "duplexer2": "0.1.4",
+        "inherits": "2.0.3",
+        "parents": "1.0.1",
+        "readable-stream": "2.3.3",
+        "resolve": "1.5.0",
+        "stream-combiner2": "1.1.1",
+        "subarg": "1.0.0",
+        "through2": "2.0.3",
+        "xtend": "4.0.1"
+      },
+      "dependencies": {
+        "concat-stream": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+          "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+          "requires": {
+            "inherits": "2.0.3",
+            "readable-stream": "2.3.3",
+            "typedarray": "0.0.6"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI="
+    },
+    "num2fraction": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1.0.2"
+      }
+    },
+    "optimist": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
+      "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
+      "requires": {
+        "wordwrap": "0.0.3"
+      }
+    },
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc="
+    },
+    "pako": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
+      "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
+    },
+    "parents": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+      "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+      "requires": {
+        "path-platform": "0.11.15"
+      }
+    },
+    "parse-asn1": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
+      "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
+      "requires": {
+        "asn1.js": "4.9.2",
+        "browserify-aes": "1.1.1",
+        "create-hash": "1.1.3",
+        "evp_bytestokey": "1.0.3",
+        "pbkdf2": "3.0.14"
+      }
+    },
+    "path-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+      "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
+    },
+    "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="
+    },
+    "path-parse": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+      "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
+    },
+    "path-platform": {
+      "version": "0.11.15",
+      "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+      "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I="
+    },
+    "pbkdf2": {
+      "version": "3.0.14",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz",
+      "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==",
+      "requires": {
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "ripemd160": "2.0.1",
+        "safe-buffer": "5.1.1",
+        "sha.js": "2.4.9"
+      }
+    },
+    "postcss": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
+      "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
+      "requires": {
+        "chalk": "2.3.0",
+        "source-map": "0.6.1",
+        "supports-color": "5.1.0"
+      }
+    },
+    "postcss-value-parser": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz",
+      "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU="
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+    },
+    "process-nextick-args": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+    },
+    "promise": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz",
+      "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=",
+      "requires": {
+        "asap": "1.0.0"
+      }
+    },
+    "public-encrypt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz",
+      "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.1.3",
+        "parse-asn1": "5.1.0",
+        "randombytes": "2.0.6"
+      }
+    },
+    "punycode": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
+    },
+    "randombytes": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
+      "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "randomfill": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz",
+      "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==",
+      "requires": {
+        "randombytes": "2.0.6",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "read-only-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
+      "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
+      "requires": {
+        "readable-stream": "2.3.3"
+      }
+    },
+    "readable-stream": {
+      "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": "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"
+      }
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+    },
+    "resolve": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
+      "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
+      "requires": {
+        "path-parse": "1.0.5"
+      }
+    },
+    "right-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+      "requires": {
+        "align-text": "0.1.4"
+      }
+    },
+    "ripemd160": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
+      "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
+      "requires": {
+        "hash-base": "2.0.2",
+        "inherits": "2.0.3"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+    },
+    "sax": {
+      "version": "0.5.8",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+      "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE="
+    },
+    "sha.js": {
+      "version": "2.4.9",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz",
+      "integrity": "sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A==",
+      "requires": {
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "shasum": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+      "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+      "requires": {
+        "json-stable-stringify": "0.0.1",
+        "sha.js": "2.4.9"
+      }
+    },
+    "shell-quote": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+      "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+      "requires": {
+        "array-filter": "0.0.1",
+        "array-map": "0.0.0",
+        "array-reduce": "0.0.0",
+        "jsonify": "0.0.0"
+      }
+    },
+    "shelljs": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+      "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E="
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "stream-browserify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+      "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      }
+    },
+    "stream-combiner2": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+      "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+      "requires": {
+        "duplexer2": "0.1.4",
+        "readable-stream": "2.3.3"
+      }
+    },
+    "stream-http": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz",
+      "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==",
+      "requires": {
+        "builtin-status-codes": "3.0.0",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3",
+        "to-arraybuffer": "1.0.1",
+        "xtend": "4.0.1"
+      }
+    },
+    "stream-splicer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz",
+      "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      }
+    },
+    "string_decoder": {
+      "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": "5.1.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+      "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E="
+    },
+    "stylus": {
+      "version": "0.54.5",
+      "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz",
+      "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=",
+      "requires": {
+        "css-parse": "1.7.0",
+        "debug": "3.1.0",
+        "glob": "7.0.6",
+        "mkdirp": "0.5.1",
+        "sax": "0.5.8",
+        "source-map": "0.1.43"
+      },
+      "dependencies": {
+        "css-parse": {
+          "version": "1.7.0",
+          "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz",
+          "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs="
+        },
+        "glob": {
+          "version": "7.0.6",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+          "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+          "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"
+          }
+        },
+        "source-map": {
+          "version": "0.1.43",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+          "requires": {
+            "amdefine": "1.0.1"
+          }
+        }
+      }
+    },
+    "subarg": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+      "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+      "requires": {
+        "minimist": "1.2.0"
+      }
+    },
+    "supports-color": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
+      "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
+      "requires": {
+        "has-flag": "2.0.0"
+      }
+    },
+    "syntax-error": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz",
+      "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=",
+      "requires": {
+        "acorn": "4.0.13"
+      }
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+    },
+    "through2": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+      "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+      "requires": {
+        "readable-stream": "2.3.3",
+        "xtend": "4.0.1"
+      }
+    },
+    "timers-browserify": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+      "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+      "requires": {
+        "process": "0.11.10"
+      }
+    },
+    "to-arraybuffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
+    },
+    "transformers": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz",
+      "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=",
+      "requires": {
+        "css": "1.0.8",
+        "promise": "2.0.0",
+        "uglify-js": "2.2.5"
+      },
+      "dependencies": {
+        "is-promise": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz",
+          "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU="
+        },
+        "promise": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz",
+          "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=",
+          "requires": {
+            "is-promise": "1.0.1"
+          }
+        },
+        "source-map": {
+          "version": "0.1.43",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+          "requires": {
+            "amdefine": "1.0.1"
+          }
+        },
+        "uglify-js": {
+          "version": "2.2.5",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz",
+          "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=",
+          "requires": {
+            "optimist": "0.3.7",
+            "source-map": "0.1.43"
+          }
+        }
+      }
+    },
+    "tty-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
+    "uglify-js": {
+      "version": "2.8.29",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+      "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+      "requires": {
+        "source-map": "0.5.7",
+        "uglify-to-browserify": "1.0.2",
+        "yargs": "3.10.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "uglify-to-browserify": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+      "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+      "optional": true
+    },
+    "umd": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz",
+      "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4="
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+        }
+      }
+    },
+    "util": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+      "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+      "requires": {
+        "inherits": "2.0.1"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+        }
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "vm-browserify": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+      "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+      "requires": {
+        "indexof": "0.0.1"
+      }
+    },
+    "void-elements": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+      "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
+    },
+    "window-size": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+      "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
+    },
+    "with": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz",
+      "integrity": "sha1-7v0VTp550sjTQXtkeo8U2f7M4U4=",
+      "requires": {
+        "acorn": "1.2.2",
+        "acorn-globals": "1.0.9"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "1.2.2",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz",
+          "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ="
+        }
+      }
+    },
+    "wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "xtend": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+    },
+    "yargs": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+      "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+      "requires": {
+        "camelcase": "1.2.1",
+        "cliui": "2.1.0",
+        "decamelize": "1.2.0",
+        "window-size": "0.1.0"
+      }
+    }
+  }
+}
index c7ca01973091fa05a39c737df3e175bbcbbafdf9..214a07f42c11bbd4b1064515d424fcd5f02e96e3 100644 (file)
@@ -1,12 +1,14 @@
 {
   "name": "bbctrl",
-  "version": "0.3.4",
-  "homepage": "https://github.com/buildbotics/bbctrl-firmware",
-  "license": "GPL 3+",
+  "version": "0.3.3",
+  "homepage": "http://buildbotics.com/",
+  "repository": "https://github.com/buildbotics/bbctrl-firmware",
+  "license": "GPL-3.0+",
   "dependencies": {
     "autoprefixer": ">=3.0.0",
     "jade": ">=1.3.0",
     "stylus": ">=0.42.3",
-    "browserify": ">=8.1.1"
+    "browserify": ">=8.1.1",
+    "jshint": ""
   }
 }
index de300698335fc416b6d54e69bbc2ddeae21c9b66..fc38c53753e45288cbe48d1da7ca5531e3460b2d 100644 (file)
@@ -66,12 +66,45 @@ html(lang="en")
               .title
                 span.left Build
                 span.right botics
-              .subtitle Machine Controller
+              .subtitle
+                | Machine Controller v{{config.version}}
+                a.upgrade-version(v-if="show_upgrade()", href="#admin")
+                  | Upgrade to v{{latestVersion}}
+                .fa.fa-check(v-if="!show_upgrade() && latestVersion",
+                  title="Firmware up to date")
 
         .content(class="{{currentView}}-view")
           component(:is="currentView + '-view'", :index="index",
             :config="config", :template="template", :state="state", keep-alive)
 
+    message.error-message(:show.sync="errorShow")
+      div(slot="header")
+        .estop(:class="{active: state.es}"): estop(@click="estop")
+        h3 ERROR: {{errorMessage}}
+
+      div(slot="body")
+        console
+
+    message(:show.sync="confirmUpgrade")
+      h3(slot="header") Upgrade Firmware?
+      div(slot="body")
+        p
+          | Are you sure you want to upgrade the firmware to version
+          | {{latestVersion}}?
+
+        p.pure-control-group
+          label(for="pass") Password
+          input(name="pass", v-model="password", type="password")
+
+      div(slot="footer")
+        button.pure-button(@click="confirmUpgrade=false") Cancel
+        button.pure-button.pure-button-primary(@click="upgrade_confirmed")
+          | Upgrade
+
+    message(:show.sync="firmwareUpgrading")
+      h3(slot="header") Firmware upgrading
+      p(slot="body") Please wait...
+
     #templates
       include ../../build/templates.jade
 
index 26875832126edab0a56430b6589fdcda4225768a..42b48fe26c868deb97ad097a01c07a2bc92d66bd 100644 (file)
@@ -54,18 +54,9 @@ script#admin-view-template(type="text/x-template")
     button.pure-button.pure-button-primary(@click="upgrade") Upgrade
 
     p
-    table.pure-table
-      tr
-        th Current version
-        td {{config.version}}
-
-      tr(v-if="latest")
-        th Latest version
-        td {{latest}}
-
-    message(:show.sync="firmwareUpgrading")
-      h3(slot="header") Firmware upgrading
-      p(slot="body") Please wait...
+      input(type="checkbox", v-model="autoCheckUpgrade",
+        @change="change_auto_check_upgrade")
+      label(for="auto-check-upgrade") &nbsp; Automatically check for upgrades
 
     message(:show.sync="hostnameSet")
       h3(slot="header") Hostname Set
diff --git a/src/jade/templates/console.jade b/src/jade/templates/console.jade
new file mode 100644 (file)
index 0000000..ec86cba
--- /dev/null
@@ -0,0 +1,20 @@
+script#console-template(type="text/x-template")
+  .console
+    .toolbar
+      button.pure-button(title="Clear console.", @click="clear")
+        .fa.fa-trash
+
+    table
+      tr
+        th Level
+        th Source
+        th Location
+        th Repeat
+        th Message
+
+      tr(v-for="msg in messages.reverse()", :class="msg.level || 'info'")
+        td {{msg.level  || 'info'}}
+        td {{msg.source || ''}}
+        td {{msg.where  || ''}}
+        td {{msg.repeat}}
+        td {{msg.msg}}
index 7a808ad2f66c61eedae9e8334ac3c4b756a74425..8d741ffb124729157161a5f175d1822b55b84c80 100644 (file)
@@ -241,27 +241,10 @@ script#control-view-template(type="text/x-template")
         center Jogging speed is set by the ring that is clicked.
 
       section#content4.tab-content
-        .toolbar
-          button.pure-button(title="Clear console.", @click="clear_console")
-            .fa.fa-trash
-
-        table.console
-          tr
-            th Level
-            th Location
-            th Code
-            th Repeat
-            th Message
-
-          tr(v-for="msg in console", :class="msg.level || 'info'")
-            td {{msg.level || 'info'}}
-            td {{msg.where || ''}}
-            td {{msg.code  || '0'}}
-            td {{msg.repeat}}
-            td {{msg.msg}}
+        console
 
       section#content5.tab-content
-        component(is="indicators", :state="state")
+        indicators(:state="state")
 
       section#content6.tab-content
         .video
index 83dd87494c397c3f889c9b00d79ff686077bfaef..658c511ed923e6698ba727bb97a4782e35a25929 100644 (file)
@@ -6,7 +6,7 @@ var api = require('./api');
 
 module.exports = {
   template: '#admin-view-template',
-  props: ['config'],
+  props: ['config', 'state'],
 
 
   data: function () {
@@ -14,7 +14,6 @@ module.exports = {
       configRestored: false,
       confirmReset: false,
       configReset: false,
-      firmwareUpgrading: false,
       hostnameSet: false,
       usernameSet: false,
       passwordSet: false,
@@ -24,7 +23,8 @@ module.exports = {
       username: '',
       current: '',
       password: '',
-      password2: ''
+      password2: '',
+      autoCheckUpgrade: true,
     }
   },
 
@@ -32,11 +32,15 @@ module.exports = {
   events: {
     connected: function () {
       if (this.firmwareUpgrading) location.reload(true);
-    }
+    },
+
+    latest_version: function (version) {this.latest = version}
   },
 
 
   ready: function () {
+    this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
+
     api.get('hostname').done(function (hostname) {
       this.hostname = hostname;
     }.bind(this));
@@ -124,7 +128,7 @@ module.exports = {
 
         try {
           config = JSON.parse(e.target.result);
-        } catch (e) {
+        } catch (ex) {
           alert("Invalid config file");
           return;
         }
@@ -154,24 +158,13 @@ module.exports = {
     },
 
 
-    check: function () {
-      $.ajax({
-        type: 'GET',
-        url: 'https://buildbotics.com/bbctrl/latest.txt',
-        cache: false
-
-      }).done(function (data) {
-        this.latest = data;
-
-      }.bind(this)).fail(function (error) {
-        alert('Failed to get latest version information');
-      });
-    },
+    check: function () {this.$dispatch('check')},
+    upgrade: function () {this.$dispatch('upgrade')},
 
 
-    upgrade: function () {
-      this.firmwareUpgrading = true;
-      api.put('upgrade');
+    change_auto_check_upgrade: function () {
+      this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
+      this.$dispatch('config-changed');
     }
   }
 }
index 0690840b73c67167db08faf110db7cc1c3b200e8..228db446f05a25573e388842bc7ac73035e8b23c 100644 (file)
@@ -4,6 +4,21 @@ var api = require('./api');
 var Sock = require('./sock');
 
 
+function compare_versions(a, b) {
+    var reStripTrailingZeros = /(\.0+)+$/;
+    var segsA = a.replace(reStripTrailingZeros, '').split('.');
+    var segsB = b.replace(reStripTrailingZeros, '').split('.');
+    var l = Math.min(segsA.length, segsB.length);
+
+    for (var i = 0; i < l; i++) {
+      var diff = parseInt(segsA[i], 10) - parseInt(segsB[i], 10);
+      if (diff) return diff;
+    }
+
+    return segsA.length - segsB.length;
+}
+
+
 module.exports = new Vue({
   el: 'body',
 
@@ -16,7 +31,15 @@ module.exports = new Vue({
       modified: false,
       template: {motors: {}, axes: {}},
       config: {motors: [{}]},
-      state: {}
+      state: {},
+      messages: [],
+      errorShow: false,
+      errorMessage: '',
+      confirmUpgrade: false,
+      firmwareUpgrading: false,
+      checkedUpgrade: false,
+      latestVersion: '',
+      password: ''
     }
   },
 
@@ -46,7 +69,35 @@ module.exports = new Vue({
 
 
     connected: function () {this.update()},
-    update: function () {this.update()}
+    update: function () {this.update()},
+
+
+    check: function () {
+      this.latestVersion = '';
+
+      $.ajax({
+        type: 'GET',
+        url: 'https://buildbotics.com/bbctrl/latest.txt',
+        data: {hid: this.state.hid},
+        cache: false
+
+      }).done(function (data) {
+        this.latestVersion = data;
+        this.$broadcast('latest_version', data);
+      }.bind(this))
+    },
+
+
+    upgrade: function () {
+      this.password = '';
+      this.confirmUpgrade = true;
+    },
+
+
+    error: function (msg) {
+      this.errorShow = true;
+      this.errorMessage = msg.msg;
+    }
   },
 
 
@@ -63,6 +114,24 @@ module.exports = new Vue({
     },
 
 
+    upgrade_confirmed: function () {
+      this.confirmUpgrade = false;
+
+      api.put('upgrade', {password: this.password}).done(function () {
+        this.firmwareUpgrading = true;
+
+      }.bind(this)).fail(function () {
+        alert('Invalid password');
+      }.bind(this));
+    },
+
+
+    show_upgrade: function () {
+      if (!this.latestVersion) return false;
+      return compare_versions(this.config.version, this.latestVersion) < 0;
+    },
+
+
     update: function () {
       $.ajax({type: 'GET', url: '/config-template.json', cache: false})
         .success(function (data, status, xhr) {
@@ -71,6 +140,14 @@ module.exports = new Vue({
           api.get('config/load').done(function (data) {
             this.config = data;
             this.parse_hash();
+
+            if (!this.checkedUpgrade) {
+              this.checkedUpgrade = true;
+
+              var check = this.config.admin['auto-check-upgrade'];
+              if (typeof check == 'undefined' || check)
+                this.$emit('check');
+            }
           }.bind(this))
         }.bind(this))
     },
@@ -82,12 +159,11 @@ module.exports = new Vue({
       this.sock.onmessage = function (e) {
         var msg = e.data;
 
-        if (typeof msg == 'object') {
-          for (var key in msg)
-            Vue.set(this.state, key, msg[key]);
-
-          if ('msg' in msg) this.$broadcast('message', msg);
-        }
+        if (typeof msg == 'object')
+          for (var key in msg) {
+            if (key == 'msg') this.$broadcast('message', msg.msg);
+            else Vue.set(this.state, key, msg[key]);
+          }
       }.bind(this);
 
       this.sock.onopen = function (e) {
@@ -100,8 +176,6 @@ module.exports = new Vue({
         this.status = 'disconnected';
         this.$broadcast(this.status);
       }.bind(this);
-
-      console.debug('Hello');
     },
 
 
diff --git a/src/js/console.js b/src/js/console.js
new file mode 100644 (file)
index 0000000..d8cb148
--- /dev/null
@@ -0,0 +1,59 @@
+'use strict'
+
+
+function _msg_equal(a, b) {
+  return a.level == b.level && a.location == b.location && a.code == b.code &&
+    a.msg == b.msg;
+}
+
+
+// Shared among all instances
+var messages = [];
+
+
+module.exports = {
+  template: '#console-template',
+
+
+  data: function () {
+    return {messages: messages}
+  },
+
+
+  events: {
+    message: function (msg) {
+      // There may be multiple instances of this module so ignore messages
+      // that have already been processed.
+      if (msg.logged) return;
+      msg.logged = true;
+
+      // Make sure we have a message level
+      msg.level = msg.level || 'info';
+
+      // Add to message log and count and collapse repeats
+      if (messages.length && _msg_equal(msg, messages[messages.length - 1]))
+        messages[messages.length - 1].repeat++;
+
+      else {
+        msg.repeat = 1;
+        messages.push(msg);
+      }
+
+      // Write message to browser console for debugging
+      var text = JSON.stringify(msg);
+      if (msg.level == 'error' || msg.level == 'critical') console.error(text);
+      else if (msg.level == 'warning') console.warn(text);
+      else if (msg.level == 'debug' && console.debug) console.debug(text);
+      else console.log(text);
+
+      // Event on errors
+      if (msg.level == 'error' || msg.level == 'critical')
+        this.$dispatch('error', msg);
+    }
+  },
+
+
+  methods: {
+    clear: function () {messages.length = 0;},
+  }
+}
index 3e4eb6f2f54677d682bc5f38dd888c76cd055bb4..32a647d53a3aa80d8dcf9f2417e0436f02f90980 100644 (file)
@@ -8,12 +8,6 @@ function _is_array(x) {
 }
 
 
-function _msg_equal(a, b) {
-  return a.level == b.level && a.location == b.location && a.code == b.code &&
-    a.msg == b.msg;
-}
-
-
 function escapeHTML(s) {
   var entityMap = {'&': '&amp;', '<': '&lt;', '>': '&gt;'};
   return String(s).replace(/[&<>]/g, function (s) {return entityMap[s];});
@@ -34,7 +28,6 @@ module.exports = {
       axes: 'xyzabc',
       gcode: [],
       history: [],
-      console: [],
       speed_override: 1,
       feed_override: 1,
       manual_home: {x: false, y: false, z: false, a: false, b: false, c: false},
@@ -62,25 +55,11 @@ module.exports = {
       var data = {};
       data[axis] = power;
       api.put('jog', data);
-    },
-
-
-    message: function (msg) {
-      if (this.console.length &&
-          _msg_equal(msg, this.console[this.console.length - 1]))
-        this.console[this.console.length - 1].repeat++;
-
-      else {
-        msg.repeat = 1;
-        this.console.push(msg);
-      }
     }
   },
 
 
-  ready: function () {
-    this.update();
-  },
+  ready: function () {this.update()},
 
 
   methods: {
@@ -99,7 +78,7 @@ module.exports = {
 
 
     get_axis_motor_id: function (axis) {
-      var axis = axis.toLowerCase();
+      axis = axis.toLowerCase();
 
       for (var i = 0; i < this.config.motors.length; i++) {
         var motor = this.config.motors[i];
@@ -121,8 +100,8 @@ module.exports = {
       if (typeof motor == 'undefined') return;
       if (typeof motor[name] != 'undefined') return motor[name];
 
-      for (var section in this.template['motors']) {
-        var sec = this.template['motors'][section];
+      for (var section in this.template.motors) {
+        var sec = this.template.motors[section];
         if (typeof sec[name] != 'undefined') return sec[name]['default'];
       }
     },
@@ -310,9 +289,6 @@ module.exports = {
     },
 
 
-    clear_console: function () {this.console = [];},
-
-
     load_video: function () {
       this.video_url = '//' + document.location.hostname + ':8000/stream/0?=' +
         Math.random();
index dda1b4b062129e212c6dad6fbd4bea66405fed63..0b7cf7f1c531f3b971f3642ab7da230eb2aff9c9 100644 (file)
@@ -1,3 +1,6 @@
+'use strict';
+
+
 module.exports = Vue.extend({
   template: '<div class="gauge"><canvas></canvas><span></span></div>',
 
index 1430f1021fd61464106bc9ed2eb0e83674f6495c..1b9cc477953c2f355481ad51b93ebac845ec9bec 100644 (file)
@@ -43,8 +43,8 @@ module.exports = {
           this.outputs = this.config.outputs;
         else this.outputs = {};
 
-        var template = this.template.outputs;
-        for (var key in template)
+        template = this.template.outputs;
+        for (key in template)
           if (!this.outputs.hasOwnProperty(key))
             this.$set('outputs["' + key + '"]', template[key].default);
       }.bind(this));
index eca6d06cd4e90dc4b6d3585565cd675da74a7af0..d36eb09b2796c644170b4fd85128776daaa2d8aa 100644 (file)
@@ -1,12 +1,16 @@
+'use strict';
+
+
 $(function() {
   // Vue debugging
   Vue.config.debug = true;
-  Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
+  //Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
 
   // Register global components
   Vue.component('templated-input', require('./templated-input'));
   Vue.component('message', require('./message'));
   Vue.component('indicators', require('./indicators'));
+  Vue.component('console', require('./console'));
 
   Vue.filter('percent', function (value, precision) {
     if (typeof precision == 'undefined') precision = 2;
index adc7d1125400d5e32333e019449b5c99ffb6dc59..71ca620820a430217dbec9eadb9a0b074bb24128 100644 (file)
@@ -36,7 +36,7 @@ module.exports = {
     slave_update: function () {
       var slave = false;
       for (var i = 0; i < this.index; i++)
-        if (this.motor['axis'] == this.config.motors[i]['axis'])
+        if (this.motor.axis == this.config.motors[i].axis)
           slave = true;
 
       var el = $(this.$el);
diff --git a/src/js/slider.js b/src/js/slider.js
deleted file mode 100644 (file)
index f25f873..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-module.exports = new Vue({
-  template: '<input type="range" v-model="value">',
-  replace: true,
-
-
-  props: {
-    value: Number,
-    min: Number,
-    max: Number,
-    step: Number
-  },
-
-
-  data: function () {
-    return {
-    }
-  },
-
-
-  compiled: function () {
-    this.$el.attributes.min = this.min;
-    this.$el.attributes.max = this.max;
-    this.$el.attributes.step = this.step;
-  }
-}
index 00fe6fea066ba6e13993481eb41a08d06b782fb9..5a00006d5e7e0f093305c7425956baa75d1849d0 100644 (file)
@@ -1,4 +1,4 @@
-'use strict'
+'use strict';
 
 
 module.exports = {
index 53e1eb15b63500330e31b8ac81b4357d4be56d70..77c2081483f94faf377061c4ca61bb862a6d9c19 100644 (file)
@@ -1,9 +1,9 @@
 import json
-import tornado.web
+from tornado.web import RequestHandler, HTTPError
 import tornado.httpclient
 
 
-class APIHandler(tornado.web.RequestHandler):
+class APIHandler(RequestHandler):
     def __init__(self, app, request, **kwargs):
         super(APIHandler, self).__init__(app, request, **kwargs)
         self.ctrl = app.ctrl
@@ -14,7 +14,7 @@ class APIHandler(tornado.web.RequestHandler):
         self.write_json('ok')
 
 
-    def delete_ok(self): raise tornado.httpclient.HTTPError(405)
+    def delete_ok(self): raise HTTPError(405)
 
 
     def put(self, *args, **kwargs):
@@ -22,7 +22,7 @@ class APIHandler(tornado.web.RequestHandler):
         self.write_json('ok')
 
 
-    def put_ok(self): raise tornado.httpclient.HTTPError(405)
+    def put_ok(self): raise HTTPError(405)
 
 
     def prepare(self):
@@ -32,7 +32,7 @@ class APIHandler(tornado.web.RequestHandler):
             try:
                 self.json = tornado.escape.json_decode(self.request.body)
             except ValueError:
-                self.send_error(400, message = 'Unable to parse JSON.')
+                raise HTTPError(400, 'Unable to parse JSON.')
 
 
     def set_default_headers(self):
index 96b1a809217096b08f4eaaeb31b639b630e11db9..b9ccdfd15a68285b157f7a36a0aa8384c24f8216 100644 (file)
@@ -43,7 +43,7 @@ class AVR():
         self.lcd_page = ctrl.lcd.add_new_page()
         self.install_page = True
 
-        ctrl.state.add_listener(lambda x: self._update_state(x))
+        ctrl.state.add_listener(self._update_state)
 
         try:
             self.sp = serial.Serial(ctrl.args.serial, ctrl.args.baud,
@@ -78,12 +78,12 @@ class AVR():
                 retry -= 1
 
                 if retry:
-                    log.error('AVR I2C communication failed, retrying: %s' % e)
+                    log.warning('AVR I2C failed, retrying: %s' % e)
                     time.sleep(0.1)
                     continue
 
                 else:
-                    log.error('AVR I2C communication failed: %s' % e)
+                    log.error('AVR I2C failed: %s' % e)
                     raise
 
 
@@ -118,7 +118,7 @@ class AVR():
             if self.ctrl.ioloop.READ & events: self._serial_read()
             if self.ctrl.ioloop.WRITE & events: self._serial_write()
         except Exception as e:
-            log.error('Serial handler error: %s', traceback.format_exc())
+            log.warning('Serial handler error: %s', traceback.format_exc())
 
 
     def _serial_write(self):
@@ -173,7 +173,7 @@ class AVR():
                     msg = json.loads(line)
 
                 except Exception as e:
-                    log.error('%s, data: %s', e, line)
+                    log.warning('%s, data: %s', e, line)
                     continue
 
                 if 'variables' in msg:
@@ -192,7 +192,7 @@ class AVR():
 
         if update:
             if 'firmware' in update:
-                log.error('AVR rebooted')
+                log.warning('firmware rebooted')
                 self.connect()
 
             self.ctrl.state.update(update)
index e4d5fb68d6f2bb23e13a52af67dfd5d45f7cd581..c35f7408b4bfd345fec087ee20ca5c21dfc85c85 100755 (executable)
@@ -45,11 +45,6 @@ def encode_axes(axes):
     return data
 
 
-def seek(switch, open, error):
-    flags = (SEEK_OPEN if open else 0) | (SEEK_ERROR if error else 0)
-    return '%c%x%x' % (SEEK, switch, flags)
-
-
 def line_number(line): return '#ln=%d' % line
 
 
index 2e638618032fb92cbae8c7240a9032ee0e7a5667..d66ecbef4cbf201087b3b55f3964f4ebb31d3851 100644 (file)
@@ -19,6 +19,7 @@ default_config = {
     "outputs": {},
     "tool": {},
     "gcode": {},
+    "admin": {},
     }
 
 
index bb2cbf956b5450332ab3e28ef9454dd9d3c9387a..039e4da618881addf7957b87596a3d8103503c82 100644 (file)
@@ -31,6 +31,7 @@ class Ctrl(object):
         self.args = args
         self.ioloop = ioloop
 
+        self.msgs = bbctrl.Messages(self)
         self.state = bbctrl.State(self)
         self.planner = bbctrl.Planner(self)
         self.i2c = bbctrl.I2C(args.i2c_port)
index 222abb67849328a4760ba4dc5fd2223a5b3f1c6b..6740b4d6d344d450a30754f1128df803b722cad9 100644 (file)
@@ -53,7 +53,7 @@ class Jog(inevent.JogHandler):
                 axes = {}
                 for i in range(len(self.v)): axes["xyzabc"[i]] = self.v[i]
                 self.ctrl.avr.jog(axes)
-            except Exception as e: log.error('Jog: %s', e)
+            except Exception as e: log.warning('Jog: %s', e)
 
         self.ctrl.ioloop.call_later(0.25, self.callback)
 
index e9d36fbad8baa5b047fa383256300c5b557af5c5..7c86305ed9f63d5679d47fd099ed0f81b979dbe7 100644 (file)
@@ -79,7 +79,7 @@ class LCD:
             self.load_page(LCDPage(self, msg))
             self._update()
         except IOError as e:
-            log.error('LCD communication failed: %s' % e)
+            log.warning('LCD communication failed: %s' % e)
 
 
     def new_screen(self):
@@ -164,8 +164,8 @@ class LCD:
             self.addr = self.addrs[self.addr_num]
             self.lcd = None
 
-            log.error('LCD communication failed, ' +
-                      'retrying on address 0x%02x: %s' % (self.addr, e))
+            log.warning('LCD communication failed, ' +
+                        'retrying on address 0x%02x: %s' % (self.addr, e))
 
             self.reset = True
             self.timeout = self.ctrl.ioloop.call_later(1, self._update)
diff --git a/src/py/bbctrl/Messages.py b/src/py/bbctrl/Messages.py
new file mode 100644 (file)
index 0000000..4ce5e64
--- /dev/null
@@ -0,0 +1,34 @@
+import os
+import logging
+import bbctrl
+
+
+log = logging.getLogger('Msgs')
+
+
+class Messages(logging.Handler):
+    def __init__(self, ctrl):
+        logging.Handler.__init__(self, logging.WARNING)
+
+        self.ctrl = ctrl
+        self.listeners = []
+
+        logging.getLogger().addHandler(self)
+
+
+    def add_listener(self, listener): self.listeners.append(listener)
+    def remove_listener(self, listener): self.listeners.remove(listener)
+
+
+    # From logging.Handler
+    def emit(self, record):
+        msg = dict(
+            level = record.levelname.lower(),
+            source = record.name,
+            msg = record.getMessage())
+
+        if hasattr(record, 'where'): msg['where'] = record.where
+        else: msg['where'] = '%s:%d' % (record.filename, record.lineno)
+
+        for listener in self.listeners:
+            listener(msg)
index 6b737e47c9dcc52b84ab4cbf5c5b0433167728c0..ee25cb684969a218a6ca09fb0230d2940aae87cf 100644 (file)
@@ -1,10 +1,13 @@
 import json
+import re
 import logging
-import camotics.gplan as gplan
+import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error
 import bbctrl.Cmd as Cmd
 
 log = logging.getLogger('Planner')
 
+reLogLine = re.compile(
+    r'^(?P<level>[A-Z])[0-9 ]:((?P<where>[^:]+:\d+:\d+):)?(?P<msg>.*)$')
 
 
 class Planner():
@@ -13,7 +16,7 @@ class Planner():
         self.lastID = -1
         self.mode = 'idle'
 
-        ctrl.state.add_listener(lambda x: self.update(x))
+        ctrl.state.add_listener(self.update)
 
         self.reset()
 
@@ -90,15 +93,22 @@ class Planner():
 
     def log(self, line):
         line = line.strip()
-        if len(line) < 3: return
-
-        if line[0] == 'I': log.info(line[3:])
-        elif line[0] == 'D': log.debug(line[3:])
-        # TODO send these to the LCD and Web
-        elif line[0] == 'W': log.warning(line[3:])
-        elif line[0] == 'E': log.error(line[3:])
-        elif line[0] == 'C': log.critical(line[3:])
-        else: raise Exception('Could not parse planner log line: ' + line)
+        m = reLogLine.match(line)
+        if not m: return
+
+        level = m.group('level')
+        msg = m.group('msg')
+        where = m.group('where')
+
+        if where is not None: extra = dict(where = where)
+        else: extra = None
+
+        if   level == 'I': log.info    (msg, extra = extra)
+        elif level == 'D': log.debug   (msg, extra = extra)
+        elif level == 'W': log.warning (msg, extra = extra)
+        elif level == 'E': log.error   (msg, extra = extra)
+        elif level == 'C': log.critical(msg, extra = extra)
+        else: log.error('Could not parse planner log line: ' + line)
 
 
     def mdi(self, cmd):
index 50847b05ca0474056cdbc8701a2326844e9f0e12..35927adc6f00d79d47776a35905103fc1f98d061 100644 (file)
@@ -42,14 +42,15 @@ class Pwr():
         flags = self.regs[FLAGS_REG]
         errors = []
 
-        if flags & UNDER_VOLTAGE_FLAG:     errors.push('under voltage')
-        if flags & OVER_VOLTAGE_FLAG:      errors.push('over voltage')
-        if flags & OVER_CURRENT_FLAG:      errors.push('over current')
-        if flags & MEASUREMENT_ERROR_FLAG: errors.push('measurement error')
-        if flags & SHUNT_OVERLOAD_FLAG:    errors.push('shunt overload')
+        # Decode error flags
+        if flags & UNDER_VOLTAGE_FLAG:     errors.append('under voltage')
+        if flags & OVER_VOLTAGE_FLAG:      errors.append('over voltage')
+        if flags & OVER_CURRENT_FLAG:      errors.append('over current')
+        if flags & MEASUREMENT_ERROR_FLAG: errors.append('measurement error')
+        if flags & SHUNT_OVERLOAD_FLAG:    errors.append('shunt overload')
 
         # Report errors
-        self.ctrl.state.set('pwr_errors', errors)
+        if errors: log.error('Power fault: ' + ', '.join(errors))
 
 
     def _update(self):
index d4e1f45e1fd51fdc930c2d8ab087e15064798b88..d98e0fddefae0e7c4ba0f61d40982ac8d80b1dd8 100644 (file)
@@ -1,4 +1,5 @@
 import logging
+import traceback
 import bbctrl
 
 
@@ -14,8 +15,7 @@ class State(object):
         self.vars = {}
         self.callbacks = {}
         self.changes = {}
-        self.next_id = 1
-        self.listeners = {}
+        self.listeners = []
         self.timeout = None
         self.machine_vars = {}
         self.machine_var_set = set()
@@ -37,12 +37,13 @@ class State(object):
     def _notify(self):
         if not self.changes: return
 
-        for listener in self.listeners.values():
+        for listener in self.listeners:
             try:
                 listener(self.changes)
 
             except Exception as e:
-                log.error('Updating listener: %s', traceback.format_exc())
+                log.warning('Updating state listener: %s',
+                            traceback.format_exc())
 
         self.changes = {}
         self.timeout = None
@@ -83,7 +84,7 @@ class State(object):
 
         if name in self.vars: return self.vars[name]
         if name in self.callbacks: return self.callbacks[name](name)
-        if default is None: log.error('State variable "%s" not found' % name)
+        if default is None: log.warning('State variable "%s" not found' % name)
         return default
 
 
@@ -93,16 +94,11 @@ class State(object):
 
 
     def add_listener(self, listener):
-        sid = self.next_id
-        self.next_id += 1
-
-        self.listeners[sid] = listener
+        self.listeners.append(listener)
         if self.vars: listener(self.vars)
 
-        return sid
-
 
-    def remove_listener(self, sid): del self.listeners[sid]
+    def remove_listener(self, listener): self.listeners.remove(listener)
 
 
     def machine_cmds_and_vars(self, data):
index 5e57a9dbde8249135c958ffa771e0c509863e979..201cf257d6372bf3d317c798b0b6c7c73f7a6fa9 100644 (file)
@@ -9,6 +9,7 @@ import shutil
 import tarfile
 import subprocess
 import socket
+from tornado.web import HTTPError
 
 import bbctrl
 
@@ -19,10 +20,36 @@ log = logging.getLogger('Web')
 def call_get_output(cmd):
     p = subprocess.Popen(cmd, stdout = subprocess.PIPE)
     s = p.communicate()[0].decode('utf-8')
-    if p.returncode: raise Exception('Command failed')
+    if p.returncode: raise HTTPError(400, 'Command failed')
     return s
 
 
+def get_username():
+    return call_get_output(['getent', 'passwd', '1001']).split(':')[0]
+
+
+def set_username(username):
+    if subprocess.call(['usermod', '-l', username, get_username()]):
+        raise HTTPError(400, 'Failed to set username to "%s"' % username)
+
+
+def check_password(password):
+    # Get current password
+    s = call_get_output(['getent', 'shadow', get_username()])
+    current = s.split(':')[1].split('$')
+
+    # Check password type
+    if current[1] != '1':
+        raise HTTPError(400, "Don't know how to update non-MD5 password")
+
+    # Check current password
+    cmd = ['openssl', 'passwd', '-salt', current[2], '-1', password]
+    s = call_get_output(cmd).strip()
+
+    if s.split('$') != current: raise HTTPError(401, 'Wrong password')
+
+
+
 class RebootHandler(bbctrl.APIHandler):
     def put_ok(self): subprocess.Popen('reboot')
 
@@ -37,53 +64,22 @@ class HostnameHandler(bbctrl.APIHandler):
                 self.write_json('ok')
                 return
 
-        self.send_error(400, message = 'Failed to set hostname: %s' % self.json)
-
-
-def get_username():
-    return call_get_output(['getent', 'passwd', '1001']).split(':')[0]
+        raise HTTPError(400, 'Failed to set hostname')
 
 
 class UsernameHandler(bbctrl.APIHandler):
     def get(self): self.write_json(get_username())
 
 
-    def put(self):
-        if 'username' in self.json:
-            username = get_username()
-
-            if subprocess.call(['usermod', '-l', self.json['username'],
-                                username]) == 0:
-                self.write_json('ok')
-                return
-
-        self.send_error(400, message = 'Failed to set username: %s' % self.json)
+    def put_ok(self):
+        if 'username' in self.json: set_username(self.json['username'])
+        else: raise HTTPError(400, 'Missing "username"')
 
 
 class PasswordHandler(bbctrl.APIHandler):
     def put(self):
         if 'current' in self.json and 'password' in self.json:
-            # Get current user name
-            username = get_username()
-
-            # Get current password
-            s = call_get_output(['getent', 'shadow', username])
-            password = s.split(':')[1].split('$')
-
-            # Check password type
-            if password[1] != '1':
-                self.send_error(400, message =
-                                "Don't know how to update non-MD5 password")
-                return
-
-            # Check current password
-            cmd = ['openssl', 'passwd', '-salt', password[2], '-1',
-                   self.json['current']]
-            s = call_get_output(cmd).strip()
-            if s.split('$') != password:
-                print('%s != %s' % (s.split('$'), password))
-                self.send_error(401, message = 'Wrong password')
-                return
+            check_password(self.json['current'])
 
             # Set password
             s = '%s:%s' % (username, self.json['password'])
@@ -97,7 +93,7 @@ class PasswordHandler(bbctrl.APIHandler):
                 self.write_json('ok')
                 return
 
-        self.send_error(400, message = 'Failed to set password')
+        raise HTTPError(401, 'Failed to set password')
 
 
 class ConfigLoadHandler(bbctrl.APIHandler):
@@ -128,11 +124,10 @@ class FirmwareUpdateHandler(bbctrl.APIHandler):
     def prepare(self): pass
 
 
-    def put(self):
+    def put_ok(self):
         # Only allow this function in dev mode
         if not os.path.exists('/etc/bbctrl-dev-mode'):
-            self.send_error(403, message = 'Not in dev mode')
-            return
+            raise HTTPError(403, 'Not in dev mode')
 
         firmware = self.request.files['firmware'][0]
 
@@ -143,11 +138,11 @@ class FirmwareUpdateHandler(bbctrl.APIHandler):
 
         subprocess.Popen(['/usr/local/bin/update-bbctrl'])
 
-        self.write_json('ok')
-
 
 class UpgradeHandler(bbctrl.APIHandler):
-    def put_ok(self): subprocess.Popen(['/usr/local/bin/upgrade-bbctrl'])
+    def put_ok(self):
+        check_password(self.json['password'])
+        subprocess.Popen(['/usr/local/bin/upgrade-bbctrl'])
 
 
 class HomeHandler(bbctrl.APIHandler):
@@ -156,7 +151,7 @@ class HomeHandler(bbctrl.APIHandler):
 
         if set_home:
             if not 'position' in self.json:
-                raise Exception('Missing "position"')
+                raise HTTPError(400, 'Missing "position"')
 
             self.ctrl.avr.home(axis, self.json['position'])
 
@@ -212,58 +207,59 @@ class JogHandler(bbctrl.APIHandler):
     def put_ok(self): self.ctrl.avr.jog(self.json)
 
 
-# Used by CAMotics
-class WSConnection(tornado.websocket.WebSocketHandler):
-    def __init__(self, app, request, **kwargs):
-        super(WSConnection, self).__init__(app, request, **kwargs)
-        self.ctrl = app.ctrl
-        self.timer = None
+# Base class for Web Socket connections
+class ClientConnection(object):
+    def __init__(self, ctrl):
+        self.ctrl = ctrl
+        self.count = 0
 
 
     def heartbeat(self):
-        self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
-        self.write_message({'heartbeat': self.count})
+        self.ctrl.ioloop.call_later(3, self.heartbeat)
+        self.send({'heartbeat': self.count})
         self.count += 1
 
 
-    def open(self):
-        self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
-        self.count = 0;
-        self.sid = self.ctrl.state.add_listener(lambda x: self.write_message(x))
+    def notify(self, msg): self.send(dict(msg = msg))
+    def send(self, msg): raise HTTPError(400, 'Not implemented')
 
 
-    def on_close(self):
-        if self.timer is not None: self.ctrl.ioloop.remove_timeout(self.timer)
-        self.ctrl.state.remove_listener(self.sid)
+    def on_open(self, *args, **kwargs):
+        self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
+        self.ctrl.state.add_listener(self.send)
+        self.ctrl.msgs.add_listener(self.notify)
+        self.is_open = True
 
 
-    def on_message(self, msg): pass
+    def on_close(self):
+        self.ctrl.ioloop.remove_timeout(self.timer)
+        self.ctrl.state.remove_listener(self.send)
+        self.ctrl.msgs.remove_listener(self.notify)
 
 
-# Used by Web frontend
-class SockJSConnection(sockjs.tornado.SockJSConnection):
-    def heartbeat(self):
-        self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
-        self.send({'heartbeat': self.count})
-        self.count += 1
+    def on_message(self, data): self.ctrl.avr.mdi(data)
 
 
-    def on_open(self, info):
-        self.ctrl = self.session.server.ctrl
+# Used by CAMotics
+class WSConnection(ClientConnection, tornado.websocket.WebSocketHandler):
+    def __init__(self, app, request, **kwargs):
+        ClientConnection.__init__(self, app.ctrl)
+        tornado.websocket.WebSocketHandler.__init__(
+            self, app, request, **kwargs)
 
-        self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
-        self.count = 0;
 
-        self.sid = self.ctrl.state.add_listener(lambda x: self.send(x))
+    def send(self, msg): self.write_message(msg)
+    def open(self): self.on_open()
 
 
-    def on_close(self):
-        self.ctrl.ioloop.remove_timeout(self.timer)
-        self.ctrl.state.remove_listener(self.sid)
+# Used by Web frontend
+class SockJSConnection(ClientConnection, sockjs.tornado.SockJSConnection):
+    def __init__(self, session):
+        ClientConnection.__init__(self, session.server.ctrl)
+        sockjs.tornado.SockJSConnection.__init__(self, session)
 
 
-    def on_message(self, data):
-        self.ctrl.avr.mdi(data)
+    def send(self, msg): sockjs.tornado.SockJSConnection.send(self, msg)
 
 
 class StaticFileHandler(tornado.web.StaticFileHandler):
index e152283738b03ce46b375f1d860f630025059979..d33cb1cb5741553a56e4a0201ff944292a2108ce 100755 (executable)
@@ -22,6 +22,7 @@ from bbctrl.Pwr import Pwr
 from bbctrl.I2C import I2C
 from bbctrl.Planner import Planner
 from bbctrl.State import State
+from bbctrl.Messages import Messages
 import bbctrl.Cmd as Cmd
 
 
@@ -90,10 +91,9 @@ def run():
     try:
         ioloop.start()
 
-    except KeyboardInterrupt:
-        on_exit()
-
-    except: log.exception('')
+    except KeyboardInterrupt: on_exit()
+    except SystemExit: raise
+    except: logging.getLogger().exception('')
 
 
 if __name__ == "__main__": run()
index 336ceb4895934938ed40d302b80eee0e166f2173..5603185f58f6bf885727bf0fe15a0030637c50f1 100644 (file)
@@ -96,8 +96,8 @@ class Event(object):
     The output is packed into a string. It is unlikely that this function
     will be required, but it might as well be here.
     """
-    tint = long(self.time)
-    tfrac = long((self.time - tint) * 1000000)
+    tsec = int(self.time)
+    tfrac = int((self.time - tsec) * 1000000)
 
     return struct.pack(_format, tsec, tfrac, self.type, self.code, self.value)
 
index ba2709cf5a4276d9e3fcbf94a869c1ffe8334e87..ac68222cf4bafb586135ab82dc005769e6a1a4f1 100644 (file)
@@ -143,7 +143,7 @@ class EventStream(object):
     return self
 
 
-  def next(self):
+  def __next__(self):
     """
     Returns the next waiting event.
 
index bfcbd4e5080807cfe27b4ad7107b91c06a4c0b7f..90cce2e28c1fc9ed9853ff924683bc572569fe70 100644 (file)
@@ -77,7 +77,7 @@ class DeviceCapabilities(object):
                       firstLine)
 
     if not match:
-      log.warning("Do not understand device ID:", line)
+      log.warning("Do not understand device ID: %s", firstLine)
       self.bus = 0
       self.vendor = 0
       self.product = 0
@@ -173,15 +173,15 @@ class DeviceCapabilities(object):
 
   def __str__(self):
     return (
-      "%s\n"
-      "Bus: %s Vendor: %s Product: %s Version: %s\n"
-      "Phys: %s\n"
-      "Sysfs: %s\n"
-      "Uniq: %s\n"
-      "Handlers: %s Event Index: %s\n"
-      "Keyboard: %s Mouse: %s Joystick: %s\n"
-      "Events: %s" % (
-        self.name, self.bus. self.vendor, self.product, self.version, self.phys,
+      ("%s\n"
+       "Bus: %s Vendor: %s Product: %s Version: %s\n"
+       "Phys: %s\n"
+       "Sysfs: %s\n"
+       "Uniq: %s\n"
+       "Handlers: %s Event Index: %s\n"
+       "Keyboard: %s Mouse: %s Joystick: %s\n"
+       "Events: %s") % (
+        self.name, self.bus, self.vendor, self.product, self.version, self.phys,
         self.sysfs, self.uniq, self.handlers, self.eventIndex, self.isKeyboard,
         self.isMouse, self.isJoystick, EvToStr(self.eventTypes)))
 
index 15f3d7e363958a01379f328617f81e4dc2324c95..a1638bb3fce3ea591d4f5e70148de6f60baea364 100644 (file)
       "type": "text",
       "default": "M2 (End program)\n"
     }
+  },
+
+  "admin": {
+    "auto-check-upgrade": {
+      "type": "bool",
+      "default": true
+    }
   }
 }
diff --git a/src/resources/css/fd-slider-tooltip.css b/src/resources/css/fd-slider-tooltip.css
deleted file mode 100644 (file)
index bbdd5b4..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*\r
-  Sample tooltip code. Only works on grade A browsers (so no IE6, 7 or 8).\r
-  \r
-  See: http://nicolasgallagher.com/multiple-backgrounds-and-borders-with-css2/ \r
-  for full info on how to style generated content & the associated pitfalls.  \r
-*/\r
-\r
-.fd-slider-handle:before,\r
-.fd-slider-handle:after\r
-        {\r
-        content:"";\r
-        /* Remove from screen */\r
-        opacity:0;\r
-        -webkit-transition-property: all;\r
-           -moz-transition-property: all;\r
-            -ms-transition-property: all;\r
-             -o-transition-property: all;\r
-                transition-property: all;        \r
-        -webkit-transition-duration: 0.3s;\r
-           -moz-transition-duration: 0.3s;\r
-            -ms-transition-duration: 0.3s;\r
-             -o-transition-duration: 0.3s;\r
-                transition-duration: 0.3s;        \r
-        -webkit-transition-delay: 0.2s;\r
-           -moz-transition-delay: 0.2s;\r
-            -ms-transition-delay: 0.2s;\r
-             -o-transition-delay: 0.2s;\r
-                transition-delay: 0.2s;\r
-        }\r
-/* \r
-   The tooltip body - as we position it above the slider and position the \r
-   tooltip arrow below it, we need to know the height of the body. This means \r
-   that multi-line tooltips are not supported.\r
-   \r
-   To support multi-line tooltips, you will need to position the tooltip below \r
-   the slider and the tooltip pointer above the tooltip body. Additionally, you \r
-   will have to set the tooltip body "height" to auto\r
-*/\r
-.fd-slider-handle:before\r
-        {\r
-        display:block;\r
-        position:absolute;\r
-        top:-30px;\r
-        left:-25px;\r
-        margin:0;\r
-        width:60px;\r
-        padding:5px;\r
-        height:14px;\r
-        line-height:12px;\r
-        font-size:10px;\r
-        text-shadow:0 1px 0 #000;\r
-        color:#fff;\r
-        background:#222;\r
-        z-index:1;\r
-        /* Use the ARIA valuetext property, set by the script, to generate the \r
-           tooltip content */    \r
-        content:attr(aria-valuetext);  \r
-        -webkit-border-radius:3px;\r
-           -moz-border-radius:3px;\r
-                border-radius:3px;\r
-        -webkit-box-shadow: 0 0 4px #aaa;\r
-           -moz-box-shadow: 0 0 4px #aaa;\r
-                box-shadow: 0 0 4px #aaa; \r
-        }\r
-.fd-slider-handle:after\r
-        {\r
-        outline:none;\r
-        content:"";  \r
-        display:block;        \r
-        position:absolute;\r
-        top:-14px;\r
-        left:50%;\r
-        margin:0 0 0 -5px;\r
-        background:#222;\r
-        z-index:2;\r
-        width:10px;\r
-        height:10px;\r
-        overflow:hidden;\r
-        /* Rotate element by 45 degress to get the "\/" pointer effect */\r
-        -webkit-transform: rotate(45deg); \r
-           -moz-transform: rotate(45deg);\r
-            -ms-transform: rotate(45deg);\r
-             -o-transform: rotate(45deg);\r
-        -webkit-box-shadow: 0 0 4px #aaa;\r
-           -moz-box-shadow: 0 0 4px #aaa;\r
-                box-shadow: 0 0 4px #aaa;\r
-        clip:rect(4px, 14px, 14px, 4px); \r
-        }\r
-/* Change opacity and position to kick in the transitions */ \r
-.fd-slider-focused .fd-slider-handle:before,\r
-.fd-slider-hover   .fd-slider-handle:before,\r
-.fd-slider-active  .fd-slider-handle:before\r
-        {\r
-        top:-25px;     \r
-        opacity:1;             \r
-        }\r
-.fd-slider-focused .fd-slider-handle:after,\r
-.fd-slider-hover   .fd-slider-handle:after,\r
-.fd-slider-active  .fd-slider-handle:after\r
-        {\r
-        top:-9px;     \r
-        opacity:1;             \r
-        }\r
-/* Remove completely for IE 6, 7 and 8 */\r
-.oldie .fd-slider-handle:before,\r
-.oldie .fd-slider-handle:after\r
-        {\r
-        display:none;\r
-        }      
\ No newline at end of file
diff --git a/src/resources/css/fd-slider.css b/src/resources/css/fd-slider.css
deleted file mode 100644 (file)
index 84d5def..0000000
+++ /dev/null
@@ -1,1004 +0,0 @@
-/* \r
-\r
-   Don't use this version of the file in a production environment. A minified \r
-   version tailored specifically to your needs can be generated in-browser by\r
-   using the /css-generator/index.html file.\r
-   \r
-   Notes for the adventurous:\r
-   \r
-   1. The script automagically adds the classname "oldie" to IE6, 7 & 8.\r
-   \r
-   2. A combination of the .oldie class and "safe css hacks" are used to target \r
-      specific IE versions. See: http://mathiasbynens.be/notes/safe-css-hacks\r
-   \r
-   3. MSHTML has been used to base64 encode the various png images used for the \r
-      drag handle in IE6 and 7. IE7 gets served one base64 encoded image sprite \r
-      whereas IE6 gets served individual base64 encoded images.\r
-       \r
-      See: http://www.phpied.com/the-proper-mhtml-syntax/ for more info on MHTML\r
-      \r
-      The base64 encoded images have been placed into their own .mht file. This\r
-      means only IE6 and 7 will ever be burdened with downloading the file.\r
-      \r
-      A Microsoft security update in July 2011 means that .mht files now have to\r
-      be delivered with the mimetype "message/rfc822". If using IIS, there is \r
-      nothing to do as IIS appears to default to using the required \r
-      "message/rfc822" mimetype, if using Apache, there are two ways in which to\r
-      configure this behaviour:\r
-      \r
-      A. You have Admin rights and can restart the Apache server\r
-      \r
-      Update the apache_root/httpd/conf/Srm.conf file and add the following \r
-      line:\r
-      \r
-      AddType message/rfc822 mht\r
-      \r
-      This method requires that you restart Apache.\r
-      \r
-      B. You can simply use an htaccess directive \r
-      \r
-      You can add the following to an .htaccess file that sits in the same \r
-      directory as the .mht file (or to an htaccess file at the root of your\r
-      website if one exists already): \r
-      \r
-      AddType message/rfc822 mht\r
-      \r
-      You do not need to restart Apache for the change to take effect.\r
-      \r
-      Yikes - I cant do either of the above!\r
-      \r
-      Don't worry, using the .mht file isn't compulsory - just replace the \r
-      various mhtml: references (found within the .oldie classes) to point to \r
-      the correct image file on your server (not forgetting to actually upload \r
-      the image files to your server of course). \r
-      \r
-      All of the relevant rules have further instructions embedded within them.\r
-       \r
-   4. All browsers but IE6 get one "normal" base64 encoded image sprite. IE6 has\r
-      to use separate images for each animation state.\r
-   \r
-   5. The drag handle is only 20px in width & height, most probably not suitable \r
-      for touch screen devices. \r
-      \r
-   6. It's painless to base64 encode your own images, use an online encoder. \r
-      See: http://www.google.com/search?q=base+64+encoder - the only problem is \r
-      that IE6 needs each frame of the handle sprite to be encoded individually.\r
-      \r
-   7. If you want to use a different image for vertical slider drag handles,\r
-      uncomment the relevant classes below (easy to spot as they all have the \r
-      classname ".fd-slider-vertical" somewhere within the declaration).\r
-       \r
-   8. As a reminder, the following HTML is being targetted by the CSS:\r
\r
-<span class="fd-slider[-vertical]" \r
-      id="fd-slider-[the associated form element id]" \r
-      role="application" \r
-      aria-disabled="false">\r
-  <span class="fd-slider-wrapper (fd-slider-[focus|hover|no-value|disabled|active])">\r
-    <span class="fd-slider-inner"></span>\r
-    <span class="fd-slider-range"></span>\r
-    <span class="fd-slider-bar"></span>\r
-    <span class="fd-slider-handle" \r
-          tabindex="0" \r
-          role="slider" \r
-          aria-valuemin="-10" \r
-          aria-valuemax="10" \r
-          aria-labelledby="[the associated form element label id]" \r
-          id="fd-slider-handle-[the associated form element id]" \r
-          aria-describedby="fd-slider-describedby" \r
-          aria-valuenow="-3.50" \r
-          aria-valuetext="-3.50">&nbsp;</span>\r
-  </span>\r
-</span>\r
-\r
-*/\r
-\r
-/*\r
-        Element: Form element associated with the slider\r
-        Notes: The styles given to the associated form element in order to hide \r
-        it within the display\r
-*/  \r
-.fd-form-element-hidden\r
-        {\r
-        display:none;\r
-        } \r
-/*\r
-        Element: Outer wrapper\r
-        Orientation: Horizontal \r
-*/ \r
-.fd-slider\r
-        {                     \r
-        width:100%;\r
-        height:20px;\r
-        margin:0 0 10px 0;\r
-        }\r
-/*\r
-        Element: Outer wrapper\r
-        Orientation: Vertical\r
-        Notes: You may wish to float the vertical sliders left or use\r
-        display:inline-block (inline-block should work even in IE6 as the slider\r
-        is constructed from span elements but don't quote me on that)\r
-*/ \r
-.fd-slider-vertical\r
-        {               \r
-        width:20px;\r
-        height:100%; \r
-        margin:0 10px 10px 0;              \r
-        }\r
-/*\r
-        Element: Outer wrapper\r
-        Orientation: Both horizontal & vertical\r
-*/ \r
-.fd-slider,\r
-.fd-slider-vertical\r
-        {\r
-        text-align:center;\r
-        display:block;\r
-        position:relative;\r
-        cursor:pointer;        \r
-        text-decoration:none;\r
-        border:0 none;\r
-        -webkit-user-select: none;\r
-         -khtml-user-select: none;\r
-           -moz-user-select: none;\r
-            -ms-user-select: none;\r
-             -o-user-select: none;\r
-                user-select: none;\r
-        }\r
-/*\r
-        Element: Outer Wrapper \r
-        Orientation: Both horizontal & vertical\r
-        Notes: IE6 & 7 need a transparent gif as a background in order for\r
-        hover events to work. This has been base64 encoded within the .mht file.\r
-        As it's not a png, no AlphaImageLoader filter is required for IE6. \r
-*/\r
-.oldie .fd-slider,\r
-.oldie .fd-slider-vertical\r
-        {\r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        *background:transparent url(mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!blank) repeat;\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the blank.gif file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        *background:transparent url(http://www.your-domain.com/the/path/to/blank.gif) repeat;\r
-        \r
-        */                \r
-        }                 \r
-/*\r
-        Element: Inner wrapper\r
-        Notes: All other DOM elements added as children to this element.\r
-*/\r
-.fd-slider-wrapper\r
-        {\r
-        position:absolute;\r
-        top:0;\r
-        left:0;\r
-        width:100%;\r
-        height:100%;\r
-        }\r
-/*\r
-        Element: Inner wrapper\r
-        Notes: IE6 needs an expression as it cannot do a height:100% on \r
-        absolutely positioned elements and it also can't position on four \r
-        corners - so we are left with a "one time evaluated" self-deleting \r
-        expression to do the dirty work.\r
-*/\r
-\r
-.oldie .fd-slider-vertical .fd-slider-wrapper\r
-        {\r
-        *clear:expression(style.height=parentNode.offsetHeight+'px',style.clear='none', 0);\r
-        }\r
-/*\r
-        Element: ieBlur shiv\r
-        Notes: Used only by IE for the onfocus "Blur" effect\r
-*/ \r
-.fd-slider-inner\r
-        {\r
-        display:none;\r
-        }\r
-/*\r
-        Element: ieBlur shiv\r
-        Orientation: Horizontal\r
-        Notes: IE6, 7 & 8 only.\r
-        Use the "Blur" filter to simulate the box-shadow - not brilliant but the \r
-        best we can do for IE. IE6 can't absolutely position on 4 sides so we \r
-        reset the right to "auto" and use a nasty expression to calculate the \r
-        width dynamically. \r
-*/ \r
-.oldie .fd-slider-inner\r
-        {        \r
-        position:absolute;        \r
-        height:2px;\r
-        border:1px solid #bbf;\r
-        background:#bbf;      \r
-        top:4px;        \r
-        bottom:auto;\r
-        left:4px;\r
-        right:12px;              \r
-        z-index:2;       \r
-        margin:0;\r
-        padding:0;\r
-        overflow:hidden;\r
-        line-height:4px; \r
-        _right:auto;\r
-        _width:expression((this.parentNode.offsetWidth - 12) + "px");\r
-        filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=3.5); \r
-        }\r
-/*\r
-        Element: ieBlur shiv\r
-        Orientation: Vertical\r
-        Notes: Reposition the "Blur" filter for the vertical slider for IE6, \r
-        7 & 8. The "Blur" filter rule cascades from the rule above so no need to \r
-        redeclare here, we just reposition.\r
-*/ \r
-.oldie .fd-slider-vertical .fd-slider-inner\r
-        {        \r
-        width:2px;\r
-        height:auto;     \r
-        bottom:12px;\r
-        right:auto; \r
-        _bottom:auto;\r
-        *clear:expression(style.height=(parentNode.offsetHeight - 8)+'px',style.clear='none', 0);\r
-        }\r
-/*\r
-        Element: ieBlur shiv\r
-        Orientation: Horizontal & Vertical\r
-        Notes: Display the "Blurred" inner div for IE6, 7 & 8 when the slider \r
-        gains focus\r
-*/ \r
-.oldie .fd-slider-focused .fd-slider-inner\r
-        {        \r
-        display:block;\r
-        }  \r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Horizontal   \r
-*/\r
-.fd-slider-bar\r
-        {\r
-        display:block;\r
-        position:absolute;\r
-        top:8px;\r
-        right:10px; \r
-        left:10px;                \r
-        z-index:2;\r
-        height:2px;\r
-        margin:0;\r
-        padding:0;\r
-        overflow:hidden;\r
-        border:1px solid #bbb;\r
-        border-bottom:1px solid #aaa;\r
-        border-right:1px solid #aaa;\r
-        border:1px solid rgba(187, 187, 187, .8);\r
-        border-bottom:1px solid rgba(170, 170, 170, .8);\r
-        border-right:1px solid rgba(170, 170, 170, .8);                   \r
-        line-height:4px;\r
-        background-color:#ddd;\r
-        background-image: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#ccc)); \r
-        background-image: -webkit-linear-gradient(top, #ececec, #ccc); \r
-        background-image:    -moz-linear-gradient(top, #ececec, #ccc); \r
-        background-image:     -ms-linear-gradient(top, #ececec, #ccc); \r
-        background-image:      -o-linear-gradient(top, #ececec, #ccc); \r
-        background-image:         linear-gradient(to bottom, #ececec, #ccc);\r
-        border-radius:2px;\r
-        -webkit-background-clip: padding-box; \r
-           -moz-background-clip: padding; \r
-                background-clip: padding-box;\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Horizontal\r
-        Notes: For IE6 & 7 & 8. IE6 does not recognise absolute positioning on \r
-        four sides (top, right, bottom & left) so we use an expression to \r
-        dynamically calculate the track bar size. Yes, it is horrible - you \r
-        don't need to remind me.\r
-*/\r
-.oldie .fd-slider-bar\r
-        {\r
-        _right:auto;        \r
-        _width:expression((this.parentNode.offsetWidth - 20) + "px");\r
-        filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffececec',endColorstr='#ffcccccc');\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Vertical\r
-*/\r
-.fd-slider-vertical .fd-slider-bar\r
-        {         \r
-        width:2px;   \r
-        top:10px;\r
-        right:auto;\r
-        bottom:10px;\r
-        left:8px;        \r
-        height:auto;\r
-        background-color:#ddd;\r
-        background-image: -webkit-gradient(linear, left top, right top, from(#ececec), to(#ccc)); \r
-        background-image: -webkit-linear-gradient(left, #ececec, #ccc); \r
-        background-image:    -moz-linear-gradient(left, #ececec, #ccc); \r
-        background-image:     -ms-linear-gradient(left, #ececec, #ccc); \r
-        background-image:      -o-linear-gradient(left, #ececec, #ccc); \r
-        background-image:         linear-gradient(to right, #ececec, #ccc);\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Vertical\r
-        Notes: For IE6 & 7 & 8. The gradient filter colour strings in AARRGGBB \r
-        format (to save you one less google). IE6 gets repositioned and alas,\r
-        an expression to calculate the height.\r
-*/\r
-.oldie .fd-slider-vertical .fd-slider-bar\r
-        {  \r
-        _bottom:auto;\r
-        *clear:expression(style.height=(parentNode.offsetHeight - 20)+'px',style.clear='none', 0);\r
-        filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#ffececec',endColorstr='#ffcccccc');\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Horizontal & Vertical\r
-        State: Focused\r
-        Notes: Drop shadow on the inner bar when focused - newer browsers get \r
-        an animation. IE6, 7 & 8 get a "Blur" filter on an inner SPAN \r
-        .fd-slider-inner instead \r
-*/\r
-.fd-slider-focused .fd-slider-bar\r
-        {\r
-        box-shadow: 0 0 6px rgba(10, 130, 170, 0.7);\r
-        -webkit-animation:fd-pulse 2s infinite;\r
-           -moz-animation:fd-pulse 2s infinite;\r
-             -o-animation:fd-pulse 2s infinite;\r
-                animation:fd-pulse 2s infinite;\r
-        } \r
-/*\r
-        Element: Inner animated range bar \r
-        Orientation: Horizontal\r
-*/\r
-.fd-slider-range\r
-        {\r
-        display:block;\r
-        position:absolute;\r
-        top:9px;\r
-        left:11px;                   \r
-        z-index:3;\r
-        height:2px;      \r
-        margin:0;\r
-        padding:0;\r
-        overflow:hidden;\r
-        line-height:2px;\r
-        background-color:#4cc;\r
-        background-image: -webkit-gradient(linear, left top, right top, from(#6cc), to(#3cf)); \r
-        background-image: -webkit-linear-gradient(left, #6cc, #3cf); \r
-        background-image:    -moz-linear-gradient(left, #6cc, #3cf); \r
-        background-image:     -ms-linear-gradient(left, #6cc, #3cf); \r
-        background-image:      -o-linear-gradient(left, #6cc, #3cf); \r
-        background-image:         linear-gradient(to right, #6cc, #3cf);\r
-        border-radius:2px;\r
-        -webkit-background-clip: padding-box; \r
-           -moz-background-clip: padding; \r
-                background-clip: padding-box;\r
-        }\r
-/*\r
-        Element: Inner range bar \r
-        Orientation: Horizontal\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-range\r
-        {\r
-        /* IE6 - is this needed? To test. */ \r
-        _left:10px;\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ff66cccc',endColorstr='#ff33ccff');\r
-        }\r
-/*\r
-        Element: Inner range bar \r
-        Orientation: Vertical\r
-*/\r
-.fd-slider-vertical .fd-slider-range\r
-        {\r
-        height:auto;\r
-        width:2px;\r
-        top:auto;        \r
-        right:auto;\r
-        bottom:11px;\r
-        left:9px;        \r
-        background-image: -webkit-gradient(linear, left top, left bottom, from(#3cf), to(#6cc)); \r
-        background-image: -webkit-linear-gradient(top, #3cf, #6cc); \r
-        background-image:    -moz-linear-gradient(top, #3cf, #6cc); \r
-        background-image:     -ms-linear-gradient(top, #3cf, #6cc); \r
-        background-image:      -o-linear-gradient(top, #3cf, #6cc); \r
-        background-image:         linear-gradient(to bottom, #3cf, #6cc);       \r
-        }\r
-/*\r
-        Element: Inner range bar \r
-        Orientation: Vertical\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-vertical .fd-slider-range\r
-        {\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#ff66cccc',endColorstr='#ff33ccff');\r
-        }     \r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal\r
-        State: Default.\r
-        Notes: The image sprite used for the handle is base64 encoded below. IE7\r
-        gets its own version within the .mht file, IE6 does not use an image\r
-        sprite and it is necessary to base64 encode individual animation frames\r
-        within the .mht file.\r
-*/\r
-.fd-slider-handle\r
-        {\r
-        position:absolute;\r
-        display:block;\r
-        padding:0;\r
-        border:0 none;\r
-        margin:0;\r
-        z-index:3;\r
-        top:0;\r
-        left:0;\r
-        width:20px;\r
-        height:20px;\r
-        outline:0 none;\r
-        background-color:transparent;\r
-        background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAABQCAYAAAAZQFV3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjFFRDg4NEVDNENDODExRTFCMTZDREIyQTZDMjlDNTQ2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjFFRDg4NEVENENDODExRTFCMTZDREIyQTZDMjlDNTQ2Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUVEODg0RUE0Q0M4MTFFMUIxNkNEQjJBNkMyOUM1NDYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MUVEODg0RUI0Q0M4MTFFMUIxNkNEQjJBNkMyOUM1NDYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz68iMNZAAAIQElEQVR42uxYW28b1xGes7tckiIp3iRboi6mbollya5DF4VsVWpaw42ABCmaogWSFH3oDyjQoijQP2AUyFOLFshbn4SqsYFabowmdaW4NhDJiKXaUuwotm6UKJK6kKIkipfl7p7OWR3aa5kiVdQoEjQLDM7u2TPfnss3MztDKKXwPC8BnvP1xQeUSnUSvGBwshl8R18Fq70PZOtx44WSn4F89hYkV6/B26ElWuIAyP4+xBKd70311tXV/bzb7+oLOcDaYiWSTSAkR0EfT9P8UDj1j8Rq/Lfw1ukx1NcOnKExs4tDbT6f75e9tc6+c05q90gEtwWRdAoyblG/k4jeVvcr76qKZePi0K9Q5ZF5pvuXLENj6xv1Pvc5n6DJyQJAQgGdjWZCuFQRwTLQ7P/W6PaLb0YBfoNd+YMAq8BR/TIpFCzhDECEgK5SeGafJAJEA0FqO+L7LgL+rhygEwE7FrMFWMqAVvY4CX6I2hoMHYDNg5es60I8Z9xXNiHd2A25PG3WInFPY9BfLRFiwePQS8AKuJEFHWA3Fl1PVOChArPT96Gp5YRDJMSFoExZ1Z8cisQ/sqOBroRnZgydMoBpGLk8mm9uDemdJzs8kkA8FgZKQMSXbFN1ZEiqQGl26eFS4cbwiKFTxvQyMPbhZPb9weHl6bsL4ZymZzRKXSKBADKbtew5+vDB8vKVwb8kPrh029A5yFIMYgO4UNrh1Nl+6H/9PBwPtUMg6DcGRBcTMDM5CzevjsDU2E3smUXZMRO7pOlh40AJoAR56+KvdxgsyiJvd/ebHinlYPlMZYPoezyTHx/a3p6xZSqHcg5fCn9ITC0xPRcvapJnLEoqASQUOczvRdM7WqQjisqfdTOwtA9M5GLhByHzMSJ/r3EghUuB92kcg5pnKHJlK4qdSxV/Lo5Tuatip5zlkjd9zBhITEtjyg5FUdx/iuVeuJQi35lTxZc2QGpkg2tAjbRJ2r9+6KGjb9bbHsqyLOzbV53RhnAwG+NcMpn0vxPVvz2pWn/sc9i6TlcRyzErCDbkZhY94CdpWhhPZO72yvnBXzeI/8RwkeDcZE5Pk0wHIOfz+ao/r6RP3M67ftLhtXb3OInFKyFXGV9xAnZ0qn0ukL2iPfS3mCri2PWfOhwTVqtV4duhC6Yly/F43H0lBa8QW1WnX6TSlqqTRQx1KHRhT/BeJw6BSl0+58mPdoXXmA4/PAOrOEPjQGKxmHtRD4aaqS5G8wRWC0B1YFvyhDXovigOpgWKftZq/0Ys9sh97Nix9SK9zLQRNzY2XEnP8QDJaSReIFTECIpbR83cZaaqUZ1q6GUF3eJnOmauPmUpu7u7FnDrwpqigygKCMimAcRMazZDjYmGY3R1T6eEpRgWUCgUVM/uZlLx1Lp8shFTCC6YFBHZwRi+hAqQ1yhYt7ZSTIdz8LGlFM1JxeXt1iUjC2FPbbMVuzFQ4SzxJX0SU9g3VJzdFm7u0VQszHT4CWtFSynao+JwOBKdCzMfp1w1L6iB5oATQ4nXQowoZwDrBMEpwSCsy4n4Wmv8848dDd4EN0N9b+57gIZ9NjU1JTvU7YmWR7dv5FcWV8NZVUurGrXjXtYiH1mbVnWajUc2XA/GP2rKbNxmOhxQNdsyAy20tbVtz8/Pz21PTV1zfBLfnm/s6rlzpKVxq7q2mg1yb69v16wtRFoj98fbIXsreOrUHNPhTuKpPWTT1TweT66rq2sdN3oaPvts2zf19xm0ngAeqpOHhjRaRRTNba6zszOMYxNMx+TSaDEEPOW+kFvyxMSEc2VlpZqRXVVVm/F1ScrV19dvNTQ0bJ85cyZdU1OjmNwXmAGfu4M1u3Ld1P5XIWA/MP1CRL0vcZ5y4cKFZuTcq6Io9qEYeYqmaTMot5Cb165fv374PGVgYOAckvdntbW1fdjKaOMicpABUuSoMjs7O5pKpX6PoJXzlJ6enrbq6upfIIG/iYA2FtlYIEMww9lin4h9F6anpy04dq1inoKzeQMBe1HJiksTstnsfgrhDy2Rg8FgP5roj/D5nbJ5isVieRk/aNnZ2SHpdFo/4HePNRa/338e2z+UzVMQsAPBBASrRGzCf0jL5yk4o1LLLHnx2ZfPUxAs7na7fThT/iN7MBiuZK0SD5Wtra37CNiJ3COMKgx034+98YwujSLgg4p5SiQSGXW5XCGkTTueNEF5Csj4qqIA8nB+aWnpmTxlP2AGfy0m7Xb7MN7/AEGDgiAQm80GbAuQJjSXywGOCSO5h8Ph8J1D5ylIif5AIHDe6/W2V1VVGXlKJpNJbG5uzkaj0ZFEIvFVnvJ/67H/p6WqWvAd/TpY7V0gWxt5qSoC+ex9SK7egbdD64cNAYLzvakTdXV13+v2u7pCDrC0WInAS1V0HNOKoXDqbmI1fhXeOj2D+nqlUlU9xpHv99Y6u845qeyRGCcfl6pIv5NYva3u0LuqIm1cHPojqkTLhQAJGlvP1vvcnT5Bk3ipipYoVYkDzf7u0e0X+9FcLvPfuZKAVnBUn0QvIPJSFS1TqhLbjvheQsC/lgO0IWCAl6r0Q5Sq/DylSx8EaPkPS1XAU+GypapNT2PQdchS1ValUlUBZqeXoKmliZeqoEypiirhmYh5/0oB5mDk8r18c2ub3nkyUKFUtVa4MXyPp7UHml4exj6cy74/OL48fTcezmnUVKoCU6lqY/nK4Fjig0ufm4P8QSGAlQbq4dTZbuh//WtwPFQPgaCLl6p2YGYyBjev3oOpsU+xJ8Yib6UQIHAq+FCO8tZeDNsoLNFZ5W1uv+mVCwHFgobNRI0C3zO2TPWrEGBc/xZgAJyadcoLu6zuAAAAAElFTkSuQmCC);\r
-        background-position:0 0;\r
-        cursor:W-resize;  \r
-        line-height:20px;\r
-        font-size:10px;       \r
-        -moz-outline:0 none;\r
-        -webkit-touch-callout: none;        \r
-          -webkit-user-select: none;\r
-           -khtml-user-select: none;\r
-             -moz-user-select: none;\r
-              -ms-user-select: none;\r
-               -o-user-select: none;\r
-                  user-select: none;                                 \r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal\r
-        State: Default\r
-        Notes: IE6, 7 & 8 use a nasty expression in order to not draw focus \r
-        outline on drag handle. \r
-*/\r
-.oldie .fd-slider-handle\r
-        {\r
-        /* IE6 & 7 - set the handle sprite as the background image */ \r
-        \r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        *background-image:url(mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!fullsprite);\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the fd-slider-sprite.png \r
-           file on your server (replace "www.your-domain.com/the/path/to/") \r
-        */\r
-                \r
-        /* \r
-        \r
-        *background-image:url(http://www.your-domain.com/the/path/to/fd-slider-sprite.png);\r
-        \r
-        */        \r
-        \r
-        /**********************************************************************/\r
-                \r
-        /* IE6 - reset the background image sprite stipulated above. */\r
-        _background-image:none;\r
-\r
-        /* \r
-           IE6 - use the AlphaImageLoader to either load a base64 encoded image \r
-           from the .mht file or a normal png image from the server \r
-        */\r
-        \r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */ \r
-        \r
-        /*\r
-               \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!handlenormal');\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the fd-handle-normal.png\r
-           file on your server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='http://www.your-domain.com/the/path/to/fd-handle-normal.png');\r
-        \r
-        */\r
-        \r
-        /* IE6, 7 & 8 */\r
-        outline:expression(hideFocus='true');\r
-        }       \r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal & Vertical\r
-        State: Focused\r
-        Notes: Attempts to remove the focus outline, remove the rule if it\r
-        offends your sensibilities.\r
-*/\r
-.fd-slider-handle:focus\r
-        {\r
-        outline:0 none;\r
-        border:0 none;\r
-        -moz-user-focus:normal;\r
-        }\r
-.fd-slider-handle:focus::-moz-focus-inner \r
-        { \r
-        border-color: transparent; \r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal\r
-        State: Focused and Hovered and also while handle is animating into \r
-        position or being dragged.\r
-        Notes: I'm using the same image for focused, hover and active states\r
-        but you can, of course, go wild.\r
-*/\r
-.fd-slider-focused .fd-slider-handle,\r
-.fd-slider-hover   .fd-slider-handle,\r
-.fd-slider-active  .fd-slider-handle\r
-        {\r
-        background-position:0 -20px;\r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal\r
-        State: Focused and Hovered and also while handle is animating into \r
-        position or being dragged.\r
-        Notes: IE6 only.\r
-*/\r
-.oldie .fd-slider-focused .fd-slider-handle,\r
-.oldie .fd-slider-hover   .fd-slider-handle,\r
-.oldie .fd-slider-active  .fd-slider-handle\r
-        {\r
-        /* \r
-           IE6 - use the AlphaImageLoader to either load a base64 encoded image \r
-           from the .mht file or a normal png image from the server \r
-        */\r
-        \r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */ \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!handleglow');\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the fd-handle-glow.png\r
-           file on your server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='http://www.your-domain.com/the/path/to/fd-handle-glow.png');\r
-\r
-        */\r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Vertical\r
-        Notes: Change the cursor to the correct icon.\r
-*/\r
-.fd-slider-vertical .fd-slider-handle\r
-        {\r
-        cursor:N-resize;\r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Vertical\r
-        Notes: IE6, 7 & 8\r
-         \r
-.oldie .fd-slider-vertical .fd-slider-handle\r
-        {\r
-        }\r
-*/\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Vertical\r
-        State: Focused and Hovered and also while handle is animating into \r
-        position or being dragged.\r
\r
-.fd-slider-vertical .fd-slider-focused .fd-slider-handle,\r
-.fd-slider-vertical .fd-slider-hover   .fd-slider-handle,\r
-.fd-slider-vertical .fd-slider-active  .fd-slider-handle\r
-        {\r
-        }\r
-*/\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Vertical\r
-        State: Focused and Hovered and also while handle is animating into \r
-        position or being dragged.\r
-        Notes: IE6, 7 & 8\r
\r
-.oldie .fd-slider-vertical .fd-slider-focused .fd-slider-handle,\r
-.oldie .fd-slider-vertical .fd-slider-hover   .fd-slider-handle,\r
-.oldie .fd-slider-vertical .fd-slider-active  .fd-slider-handle\r
-        {\r
-        }\r
-*/\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal & Vertical\r
-        State: User has not yet used the slider to set a value\r
-        Notes: I screwed the positioning of the image sprite by 1px which is why\r
-        it's -59px and not -60px. Yeah - I suck at Photoshop.      \r
-*/\r
-.fd-slider-no-value .fd-slider-handle\r
-        {\r
-        background-position:0 -59px;\r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Horizontal\r
-        State: User has not yet used the slider to set a value\r
-        Notes: IE6 only\r
-*/\r
-.oldie .fd-slider-no-value .fd-slider-handle\r
-        {\r
-        /* \r
-           IE6 - use the AlphaImageLoader to either load a base64 encoded image \r
-           from the .mht file or a normal png image from the server \r
-        */\r
-        \r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */ \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!handlenovalue');\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the fd-handle-no-value.png\r
-           file on your server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='http://www.your-domain.com/the/path/to/fd-handle-no-value.png');\r
-\r
-        */\r
-        }\r
-/*\r
-        Element: Drag handle \r
-        Orientation: Vertical\r
-        State: User has not yet used the slider to set a value\r
-        Notes: IE6, 7 & 8. Only required should you use a different image for\r
-        vertical sliders.\r
-        \r
-.oldie .fd-slider-vertical .fd-slider-no-value .fd-slider-handle\r
-        {\r
-        }        \r
-*/ \r
-/*\r
-        Element: document.body\r
-        Orientation: Horizontal and  Vertical\r
-        Notes: Class given to body to change cursor style when dragging. It also\r
-        attempts to stop text selection while dragging. \r
-*/\r
-body.fd-slider-drag-horizontal,\r
-body.fd-slider-drag-horizontal *,\r
-body.fd-slider-drag-vertical,\r
-body.fd-slider-drag-vertical *\r
-        {\r
-        cursor:N-resize !important;\r
-        -webkit-user-select: none;\r
-           -moz-user-select: none;\r
-            -ms-user-select: none;\r
-             -o-user-select: none;\r
-                user-select: none;\r
-        }\r
-/*\r
-        Element: document.body\r
-        Orientation: Horizontal\r
-        Notes: Class given to body to change cursor style when dragging \r
-*/\r
-body.fd-slider-drag-horizontal,\r
-body.fd-slider-drag-horizontal *\r
-        {\r
-        cursor:W-resize !important;\r
-        }\r
-/*\r
-        Element: Inner wrapper\r
-        Orientation: Horizontal & Vertical\r
-        State: disabled\r
-        Notes: Class given to slider when disabled\r
-*/  \r
-.fd-slider-disabled\r
-        {\r
-        opacity:.8;\r
-        cursor:default;\r
-        }\r
-/*\r
-        Element: Drag handle\r
-        Orientation: Horizontal\r
-        State: disabled\r
-        Notes: Class given to slider when disabled\r
-*/ \r
-.fd-slider-disabled .fd-slider-handle\r
-        {\r
-        cursor:default !important;\r
-        background-position:0 -40px;\r
-        opacity:1;\r
-        }\r
-/*\r
-        Element: Drag handle\r
-        Orientation: Horizontal\r
-        State: disabled\r
-        Notes: IE6 only\r
-*/ \r
-.oldie .fd-slider-disabled .fd-slider-handle\r
-        {\r
-        /* \r
-           IE6 - use the AlphaImageLoader to either load a base64 encoded image \r
-           from the .mht file or a normal png image from the server \r
-        */\r
-        \r
-        /* \r
-           If using the .mht file then uncomment the following rule and edit the \r
-           filepath to match the absolute path to the fd-slider.mht file on your \r
-           server (replace "www.your-domain.com/the/path/to/") \r
-        */ \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='mhtml:http://www.your-domain.com/the/path/to/fd-slider.mht!handledisabled');\r
-        \r
-        */\r
-        \r
-        /* \r
-           If not using the .mht file then uncomment the following rule and edit \r
-           the filepath to match the absolute path to the fd-handle-disabled.png\r
-           file on your server (replace "www.your-domain.com/the/path/to/") \r
-        */        \r
-        \r
-        /*\r
-        \r
-        _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod=crop,src='http://www.your-domain.com/the/path/to/fd-handle-disabled.png');\r
-\r
-        */\r
-        }\r
-/*\r
-        Element: Drag handle\r
-        Orientation: Vertical\r
-        State: disabled\r
-        \r
-.fd-slider-vertical .fd-slider-disabled .fd-slider-handle\r
-        {\r
-        }\r
-.oldie .fd-slider-vertical .fd-slider-disabled .fd-slider-handle\r
-        {\r
-        }\r
-*/ \r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Horizontal\r
-        State: disabled\r
-*/ \r
-.fd-slider-disabled .fd-slider-bar\r
-        {\r
-        cursor:auto !important;\r
-        border:1px solid #888;\r
-        border-bottom:1px solid #999;\r
-        border-right:1px solid #999;\r
-        border:1px solid rgba(136,136,136,.8);\r
-        border-bottom:1px solid rgba(153,153,153,.8);\r
-        border-right:1px solid rgba(153,153,153,.8);\r
-        background-color:#555;\r
-        background-image: -webkit-gradient(linear, left top, right top, from(#666), to(#333)); \r
-        background-image: -webkit-linear-gradient(left, #666, #333); \r
-        background-image:    -moz-linear-gradient(left, #666, #333); \r
-        background-image:     -ms-linear-gradient(left, #666, #333); \r
-        background-image:      -o-linear-gradient(left, #666, #333); \r
-        background-image:         linear-gradient(to right, #666, #333);\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Vertical\r
-        State: disabled\r
-*/ \r
-.fd-slider-vertical .fd-slider-disabled .fd-slider-bar\r
-        {\r
-        background-image: -webkit-gradient(linear, left top, right bottom, from(#333), to(#666)); \r
-        background-image: -webkit-linear-gradient(top, #333, #666); \r
-        background-image:    -moz-linear-gradient(top, #333, #666); \r
-        background-image:     -ms-linear-gradient(top, #333, #666); \r
-        background-image:      -o-linear-gradient(top, #333, #666); \r
-        background-image:         linear-gradient(to bottom, #333, #666);\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Horizontal\r
-        State: disabled\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-disabled .fd-slider-bar\r
-        {\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#ff666666',endColorstr='#ff333333');\r
-        }\r
-/*\r
-        Element: Inner track bar\r
-        Orientation: Vertical\r
-        State: disabled\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-vertical .fd-slider-disabled .fd-slider-bar\r
-        {\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ff666666',endColorstr='#ff333333');\r
-        }\r
-/*\r
-        Element: Range bar\r
-        Orientation: Horizontal\r
-        State: disabled\r
-*/\r
-.fd-slider-disabled .fd-slider-range\r
-        {\r
-        cursor:auto !important;\r
-        background-color:#222;\r
-        background-image: -webkit-gradient(linear, left top, right top, from(#222), to(#000)); \r
-        background-image: -webkit-linear-gradient(left, #222, #000); \r
-        background-image:    -moz-linear-gradient(left, #222, #000); \r
-        background-image:     -ms-linear-gradient(left, #222, #000); \r
-        background-image:      -o-linear-gradient(left, #222, #000); \r
-        background-image:         linear-gradient(to right, #222, #000);\r
-        }\r
-/*\r
-        Element: Range bar\r
-        Orientation: Horizontal\r
-        State: disabled\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-disabled .fd-slider-range\r
-        {\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#ff222222',endColorstr='#ff000000');\r
-        }\r
-/*\r
-        Element: Range bar\r
-        Orientation: Vertical\r
-        State: disabled\r
-*/\r
-.fd-slider-vertical .fd-slider-disabled .fd-slider-range\r
-        {\r
-        background-image: -webkit-gradient(linear, left top, right bottom, from(#000), to(#222)); \r
-        background-image: -webkit-linear-gradient(top, #000, #222); \r
-        background-image:    -moz-linear-gradient(top, #000, #222); \r
-        background-image:     -ms-linear-gradient(top, #000, #222); \r
-        background-image:      -o-linear-gradient(top, #000, #222); \r
-        background-image:         linear-gradient(to bottom, #000, #222);     \r
-        }\r
-/*\r
-        Element: Range bar\r
-        Orientation: Vertical\r
-        State: disabled\r
-        Notes: IE6, 7 & 8\r
-*/\r
-.oldie .fd-slider-vertical .fd-slider-disabled .fd-slider-range\r
-        {\r
-        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ff222222',endColorstr='#ff000000');\r
-        }\r
-/* \r
-        The various prefixed keyframe rules for the glow effect used whenever\r
-        the slider gains keyboard focus\r
-*/\r
-@-webkit-keyframes fd-pulse {\r
-0%      {\r
-        box-shadow:0 0 3px rgba(100, 130, 170, 0.55);\r
-        }\r
-20%     {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-40%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-60%     {\r
-        box-shadow:0 0 6px rgba(10, 130, 170, 0.7);\r
-        }\r
-80%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-100%    {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-}\r
-@-moz-keyframes fd-pulse {\r
-0%      {\r
-        box-shadow:0 0 3px rgba(100, 130, 170, 0.55);\r
-        }\r
-20%     {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-40%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-60%     {\r
-        box-shadow:0 0 6px rgba(10, 130, 170, 0.7);\r
-        }\r
-80%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-100%    {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-}\r
-@-o-keyframes fd-pulse {\r
-0%      {\r
-        box-shadow:0 0 3px rgba(100, 130, 170, 0.55);\r
-        }\r
-20%     {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-40%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-60%     {\r
-        box-shadow:0 0 6px rgba(10, 130, 170, 0.7);\r
-        }\r
-80%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-100%    {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-}\r
-@keyframes fd-pulse {\r
-0%      {\r
-        box-shadow:0 0 3px rgba(100, 130, 170, 0.55);\r
-        }\r
-20%     {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-40%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-60%     {\r
-        box-shadow:0 0 6px rgba(10, 130, 170, 0.7);\r
-        }\r
-80%     {\r
-        box-shadow:0 0 5px rgba(40, 130, 170, 0.65);\r
-        }\r
-100%    {\r
-        box-shadow:0 0 4px rgba(70, 130, 170, 0.6);\r
-        }\r
-}\r
diff --git a/src/resources/js/fd-slider.min.js b/src/resources/js/fd-slider.min.js
deleted file mode 100644 (file)
index 264cae7..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! Unobtrusive Slider Control / HTML5 Input Range polyfill - MIT/GPL2 @freqdec */
-var fdSlider=function(){function ut(n){function nf(n){n=!!n,n!=ft&&(ft=n,pt(kt()))}function nu(){if(!p||d=="select")return;var n=g(e);if(n.min==tt&&n.max==st&&n.step==nt)return;tt=+n.min,st=+n.max,ut=tt,it=st,nt=+n.step,ir=Math.abs(st-tt),li=nt*2,bi=Math.ceil(ir/nt),ft=!1,ar(tt,st)}function gr(n){if(oi&&!n)return;try{di(l,-1),i(l,"focus",fu),i(l,"blur",su),o?i(l,"keypress",yi):(i(l,"keydown",yi),i(l,"keypress",ou)),i(a,"mouseover",br),i(a,"mouseout",sr),i(a,"mousedown",fi),i(a,"touchstart",fi),c&&(window.addEventListener&&!window.devicePixelRatio?window.removeEventListener("DOMMouseScroll",at,!1):(i(document,"mousewheel",at),i(window,"mousewheel",at)))}catch(t){}u(v,"fd-slider-focused"),u(v,"fd-slider-active"),f(v,"fd-slider-disabled"),a.setAttribute("aria-disabled",!0),e.disabled=oi=!0,clearTimeout(ht),n||ot("disable")}function hu(n){if(!oi&&!n)return;di(l,0),t(l,"focus",fu),t(l,"blur",su),o?t(l,"keypress",yi):(t(l,"keydown",yi),t(l,"keypress",ou)),t(a,"touchstart",fi),t(a,"mousedown",fi),t(a,"mouseover",br),t(a,"mouseout",sr),u(v,"fd-slider-disabled"),a.setAttribute("aria-disabled",!1),e.disabled=oi=gt=!1,n||ot("enable")}function ku(){clearTimeout(ht),ri=vt=l=a=v=ht=null,ot("destroy"),ki=null}function ii(){wr();try{var t=a.offsetWidth,n=a.offsetHeight,r=l.offsetWidth,i=l.offsetHeight,o=vt.offsetHeight,s=vt.offsetWidth,u=b?n-i:t-r;ct=u/bi,ni=Math.max(et?er(tr(ut)):Math.abs((ut-tt)/nt)*ct,0),bt=Math.min(et?er(tr(it)):Math.abs((it-tt)/nt)*ct,Math.floor(b?n-i:t-r)),ru=t,dr=n,pt(nr?kt():d=="select"?e.selectedIndex:parseFloat(e.value),!1)}catch(f){}ot("redraw")}function ot(n){var r,i,u,t;if(wt){if(n.match(/^(blur|focus|change)$/i))if(typeof document.createEvent!="undefined")t=document.createEvent("HTMLEvents"),t.initEvent(n,!0,!0),e.dispatchEvent(t);else if(typeof document.createEventObject!="undefined")try{t=document.createEventObject(),e.fireEvent("on"+n.toLowerCase(),t)}catch(f){}}else if(ki.hasOwnProperty(n))for(r={userSet:ft,disabled:oi,elem:e,value:d=="select"?e.options[e.selectedIndex].value:e.value},i=0;u=ki[n][i];i++)u.call(e,r)}function fu(){return f(v,"fd-slider-focused"),h.onfocus&&(ft=!0,pt(kt())),c&&(t(window,"DOMMouseScroll",at),t(document,"mousewheel",at),o||t(window,"mousewheel",at)),ot("focus"),!0}function su(){u(v,"fd-slider-focused"),c&&(i(document,"mousewheel",at),i(window,"DOMMouseScroll",at),o||i(window,"mousewheel",at)),lt=!0,ot("blur")}function at(n){if(!lt)return;n=n||window.event;var t=0,i;n.wheelDelta?(t=n.wheelDelta/120,o&&window.opera.version()<9.2&&(t=-t)):n.detail&&(t=-n.detail/3),b&&(t=-t),t&&(i=kt(),i+=t<0?-nt:nt,ft=!0,pt(hi(i))),s(n)}function ou(n){return n=n||window.event,n.keyCode>=33&&n.keyCode<=40||!lt||n.keyCode==45||n.keyCode==46?k(n):!0}function yi(n){if(!lt)return!0;n=n||window.event;var t=n.keyCode!==null?n.keyCode:n.charCode,i;if(t<33||t>40&&t!=45&&t!=46)return!0;i=kt(),t==37||t==40||t==46||t==34?i-=n.ctrlKey||t==34?+li:+nt:t==39||t==38||t==45||t==33?i+=n.ctrlKey||t==33?+li:+nt:t==35?i=it:t==36&&(i=ut),ft=!0,pt(hi(i)),ot("update"),s(n)}function br(){f(v,"fd-slider-hover")}function sr(){u(v,"fd-slider-hover")}function fi(n){var u,r;n=n||window.event,s(n),n.target?u=n.target:n.srcElement&&(u=n.srcElement),u&&u.nodeType==3&&(u=u.parentNode);if(n.touches){if(n.targetTouches&&n.targetTouches.length!=1)return!1;n=n.touches[0],gt=!0}return clearTimeout(ht),ht=null,lt=!1,ft=!0,u.className.search("fd-slider-handle")!=-1?(rr=b?n.clientY:n.clientX,tu=parseInt(b?l.offsetTop:l.offsetLeft)||0,ci(n),gt?(t(document,"touchmove",ci),t(document,"touchend",vi),i(a,"mousedown",fi)):(t(document,"mousemove",ci),t(document,"mouseup",vi)),f(v,"fd-slider-active"),f(document.body,"fd-slider-drag-"+(b?"vertical":"horizontal")),ot("dragstart")):(wr(),r=0,n.pageX||n.pageY?r=b?n.pageY:n.pageX:(n.clientX||n.clientY)&&(r=b?n.clientY+document.body.scrollTop+document.documentElement.scrollTop:n.clientX+document.body.scrollLeft+document.documentElement.scrollLeft),r-=b?hr+Math.round(l.offsetHeight/2):iu+Math.round(l.offsetWidth/2),r=pi(r),pr=="tween"?(f(v,"fd-slider-active"),bu(r)):pr=="timed"?(f(v,"fd-slider-active"),t(document,gt?"touchend":"mouseup",cr),ui=r,yr()):ai(r)),!1}function cr(n){return n=n||window.event,s(n),i(document,gt?"touchend":"mouseup",cr),u(v,"fd-slider-active"),clearTimeout(ht),ht=null,lt=!0,!1}function vi(n){return n=n||window.event,s(n),gt?(i(document,"touchmove",ci),i(document,"touchend",vi)):(i(document,"mousemove",ci),i(document,"mouseup",vi)),lt=!0,u(document.body,"fd-slider-drag-"+(b?"vertical":"horizontal")),u(v,"fd-slider-active"),ot("dragend"),!1}function ci(n){n=n||window.event,s(n);if(n.touches){if(n.targetTouches&&n.targetTouches.length!=1)return!1;n=n.touches[0]}return ai(pi(tu+(b?n.clientY-rr:n.clientX-rr))),!1}function ei(n){var t=kt();ft=!0,t+=n*nt,pt(hi(t))}function wr(){var i=0,t=0,n=a;try{do i+=n.offsetLeft,t+=n.offsetTop;while(n=n.offsetParent)}catch(r){}iu=i,hr=t}function yr(){var n=parseInt(b?l.offsetTop:l.offsetLeft,10);n=Math.round(ui<n?Math.max(ui,Math.floor(n-ct)):Math.min(ui,Math.ceil(n+ct))),ai(pi(n)),n!=ui?ht=setTimeout(yr,bi>20?50:100):(lt=!0,u(v,"fd-slider-active"),ot("finalise"))}function bu(n){lt=!1,or=parseInt(n,10),fr=parseInt(b?l.offsetTop:l.offsetLeft,10),uu=or-fr,lu=20,ur=0,ht||(ht=setTimeout(gi,20))}function si(n){return isNaN(n)||n===""||typeof n=="undefined"?(ft=!1,yt):n<Math.min(ut,it)?(ft=!1,Math.min(ut,it)):n>Math.max(ut,it)?(ft=!1,Math.max(ut,it)):(ft=!0,n)}function kt(){return hi(d=="input"?parseFloat(e.value):e.selectedIndex)}function hi(n){return isNaN(n)||n===""||typeof n=="undefined"?yt:Math.min(Math.max(n,Math.min(ut,it)),Math.max(ut,it))}function ai(n){var t=hi(et?du(tf(n)):b?st-Math.round(n/ct)*nt:tt+Math.round(n/ct)*nt);l.style[b?"top":"left"]=(n||0)+"px",lr(),vr(d=="select"||nt==1?Math.round(t):t)}function pt(n,t){var r=!1,i;(typeof n=="undefined"||isNaN(n)||n==="")&&d=="input"&&!nr?(i=yt,r=!0,ft=!1):i=si(n),l.style[b?"top":"left"]=(et?er(tr(i)):b?Math.round((st-i)/nt*ct):Math.round((i-tt)/nt*ct))+"px",lr(),!0&&vr(r?"":i)}function pi(n){if(et)return Math.max(Math.min(bt,n),ni);var t=n%ct;return t&&t>=ct/2?n+=ct-t:n-=t,n<Math.min(Math.abs(ni),Math.abs(bt))?n=Math.min(Math.abs(ni),Math.abs(bt)):n>Math.max(Math.abs(ni),Math.abs(bt))&&(n=Math.max(Math.abs(ni),Math.abs(bt))),Math.min(Math.max(n,0),bt)}function du(n){var i=0,r=tt,u,t;for(t in et){if(!et.hasOwnProperty(t))continue;n<i||n>+t||(u=r+(n-i)*(+et[t]-r)/(+t-i)),i=+t,r=+et[t]}return u}function tr(n){var r=0,i=tt,u=0,t;for(t in et){if(!et.hasOwnProperty(t))continue;n<i||n>+et[t]||(u=r+(n-i)*(+t-r)/(+et[t]-i)),r=+t,i=+et[t]}return u}function er(n){return(a[b?"offsetHeight":"offsetWidth"]-l[b?"offsetHeight":"offsetWidth"])/100*n}function tf(n){return n/((a[b?"offsetHeight":"offsetWidth"]-a[l?"offsetHeight":"offsetWidth"])/100)}function vr(n){ot("update"),ft?u(v,"fd-slider-no-value"):f(v,"fd-slider-no-value");if(d=="select")try{n=parseInt(n,10);if(e.selectedIndex===n){dt();return}e.options[n].selected=!0}catch(t){}else{n===""||wi||(n=(tt+Math.round((+n-tt)/nt)*nt).toFixed(yu));if(e.value===n){dt();return}e.value=n}dt(),ot("change")}function ar(n,t){ut>it?(n=Math.min(tt,Math.max(n,t)),t=Math.max(st,Math.min(n,t)),ut=Math.max(n,t),it=Math.min(n,t)):(n=Math.max(tt,Math.min(n,t)),t=Math.min(st,Math.max(n,t)),ut=Math.min(n,t),it=Math.max(n,t)),yt<Math.min(ut,it)?yt=Math.min(ut,it):yt>Math.max(ut,it)&&(yt=Math.max(ut,it)),l.setAttribute("aria-valuemin",ut),l.setAttribute("aria-valuemax",it),si(d=="input"?parseFloat(e.value):e.selectedIndex),ii()}function lr(){if(y)return;b?ti.style.height=Math.max(1,vt.offsetHeight-l.offsetTop)+"px":ti.style.width=Math.max(1,l.offsetLeft)+"px"}function cu(){for(var t=!1,r=document.getElementsByTagName("label"),n,i=0;n=r[i];i++)if(n.htmlFor&&n.htmlFor==e.id||n.getAttribute("for")==e.id){t=n;break}return t&&!t.id&&(t.id=e.id+"_label"),t}function dt(){var n=d=="select"?e.options[e.selectedIndex].value:e.value,t=kr?kr(n):d=="select"?e.options[e.selectedIndex].text?e.options[e.selectedIndex].text:n:n;l.setAttribute("aria-valuenow",n),l.setAttribute("aria-valuetext",t)}function wu(){ft=!0,wi=gu,pt(d=="input"?parseFloat(e.value):e.selectedIndex),dt(),wi=!1}function pu(){d=="input"?e.value=e.defaultValue:e.selectedIndex=au,si(d=="select"?e.options[e.selectedIndex].value:e.value),ii(),dt()}function di(n,t){n.setAttribute("tabIndex",t),n.tabIndex=t}var e=n.inp,oi=!1,d=e.tagName.toLowerCase(),tt=+n.min,st=+n.max,ut=+n.min,it=+n.max,ir=Math.abs(st-tt),nt=d=="select"?1:+n.step,li=n.maxStep?+n.maxStep:nt*2,yu=n.precision||0,bi=Math.ceil(ir/nt),et=n.scale||!1,rf=!!n.hideInput,pr=n.animation||"",b=!!n.vertical,ki=n.callbacks||{},vu=n.classNames||"",wt=!!n.html5Shim,yt=st<tt?tt:tt+(st-tt)/2,au=d=="select"?e.selectedIndex:e.defaultValue||yt,nr=wt||!!n.forceValue,eu=wt&&b&&"inpHeight"in n?n.inpHeight:!1,kr=!wt&&n.ariaFormat?n.ariaFormat:!1,gu=!wt&&!(d=="select")&&"userSnap"in n?!!n.userSnap:!1,wi=!1,ht=null,lt=!0,ff=d=="select"?e.selectedIndex:e.value,dr=0,ru=0,or=0,fr=0,uu=0,lu=0,ur=0,iu=0,hr=0,bt=0,ni=0,tu=0,ui=0,rr=0,ct=0,ft=!1,gt=!1,a,v,ri,l,ti,vt,gi;return d=="input"&&nr&&!e.defaultValue&&(e.defaultValue=kt()),st<tt&&(nt=-Math.abs(nt),li=-Math.abs(li)),et&&(et[100]=st),gi=function(){ur++;var r=uu,n=20,t=ur,i=fr,f=Math.ceil(t==n?i+r:r*(-Math.pow(2,-10*t/n)+1)+i);ai(t==n?or:f),t!=n?(ot("move"),ht=setTimeout(gi,20)):(clearTimeout(ht),ht=null,lt=!0,u(v,"fd-slider-focused"),u(v,"fd-slider-active"),ot("finalise"))},(function(){if(wt||rf){f(e,"fd-form-element-hidden");try{e.type!="range"&&r(e,"type")=="range"&&document.defaultView.getComputedStyle(e,null).getPropertyValue("display")=="inline-block"&&(e.type="number")}catch(i){}}else t(e,"change",wu);wt&&(e.setAttribute("fd-range-enabled",1),e.stepUp=function(n){ei(n||1)},e.stepDown=function(n){ei(n||-1)},p&&t(e,typeof e.onpropertychange=="object"?"propertychange":"DOMAttrModified",nu)),a=document.createElement("span"),a.className="fd-slider"+(b?"-vertical ":" ")+vu,a.id="fd-slider-"+e.id,b&&eu&&(a.style.height=eu+"px"),v=document.createElement("span"),v.className="fd-slider-wrapper"+(wt?"":" fd-slider-no-value"),ri=document.createElement("span"),ri.className="fd-slider-inner",vt=document.createElement("span"),vt.className="fd-slider-bar",rt?l=document.createElement("span"):(l=document.createElement("a"),l.setAttribute("href","#"),t(l,"click",k)),di(l,0),l.className="fd-slider-handle",l.appendChild(document.createTextNode(String.fromCharCode(160))),v.appendChild(ri),y||(ti=document.createElement("span"),ti.className="fd-slider-range",v.appendChild(ti)),v.appendChild(vt),v.appendChild(l),a.appendChild(v),e.parentNode.insertBefore(a,e),o&&(l.unselectable="on",vt.unselectable="on",ri.unselectable="on",a.unselectable="on",v.unselectable="on",y||(ti.unselectable="on")),a.setAttribute("role","application"),l.setAttribute("role","slider"),l.setAttribute("aria-valuemin",d=="select"?e.options[0].value:tt),l.setAttribute("aria-valuemax",d=="select"?e.options[e.options.length-1].value:st);var n=cu();if(n){l.setAttribute("aria-labelledby",n.id),l.id="fd-slider-handle-"+e.id;/*@if(@_win32)n.setAttribute("htmlFor",l.id);@else@*/n.setAttribute("for",l.id);/*@end@*/}document.getElementById(w)&&l.setAttribute("aria-describedby",w),e.getAttribute("disabled")==!0||e.getAttribute("disabled")=="disabled"?gr(!0):hu(!0),h.onvalue&&(ft=!0,si(d=="input"?parseFloat(e.value):e.selectedIndex)),e.form&&t(e.form,"reset",pu),dt(),ot("create"),ii()})(),{onResize:function(){(a.offsetHeight!=dr||a.offsetWidth!=ru)&&ii()},destroy:function(){ku()},reset:function(){pt(d=="input"?parseFloat(e.value):e.selectedIndex)},stepUp:function(n){ei(Math.abs(n)||1)},stepDown:function(n){ei(-Math.abs(n)||-1)},increment:function(n){ei(n)},disable:function(){gr()},enable:function(){hu()},setRange:function(n,t){ar(n,t)},getValueSet:function(){return!!ft},setValueSet:function(n){nf(n)},rescan:function(){nu()},checkValue:function(){h.onvalue&&(ft=!0,si(d=="input"?parseFloat(e.value):e.selectedIndex)),dt(),ii()}}}var n={},lt=0,c=!0,rt=!0,w="fd-slider-describedby",h={onfocus:!0,onvalue:!0},y=!1,ft="jump",p=!1,o=Object.prototype.toString.call(window.opera)==="[object Opera]",v=/^([\-]{0,1}[0-9]+(\.[0-9]+){0,1})$/,b=/^([0-9]+(\.[0-9]+){0,1})$/,ot=function(n){if(typeof n!="string"||n==="")return{};try{if(typeof JSON=="object"&&typeof JSON.parse=="function")return JSON.parse(n);if(/mousewheelenabled|fullaria|describedby|norangebar|html5animation|varsetrules/.test(n.toLowerCase())){var t=Function(["var document,top,self,window,parent,Number,Date,Object,Function,","Array,String,Math,RegExp,Image,ActiveXObject;","return (",n.replace(/<\!--.+-->/gim,"").replace(/\bfunction\b/g,"function-"),");"].join(""));return t()}}catch(i){}return{err:"Could not parse the JSON object"}},it=function(n){if(typeof n!="object")return;for(var t in n){value=n[t];switch(t.toLowerCase()){case"mousewheelenabled":c=!!value;break;case"fullaria":rt=!!value;break;case"describedby":w=String(value);break;case"norangebar":y=!!value;break;case"html5animation":ft=String(value).search(/^(jump|tween|timed)$/i)!=-1?String(value).toLowerCase():"jump";break;case"watchattributes":p=!!value;break;case"varsetrules":"onfocus"in value&&(h.onfocus=!!value.onfocus),"onvalue"in value&&(h.onvalue=!!value.onvalue)}}},t=function(n,t,i){n.addEventListener?n.addEventListener(t,i,!0):n.attachEvent&&n.attachEvent("on"+t,i)},i=function(n,t,i){try{n.removeEventListener?n.removeEventListener(t,i,!0):n.detachEvent&&n.detachEvent("on"+t,i)}catch(r){}},k=function(n){n=n||window.event,n.stopPropagation&&(n.stopPropagation(),n.preventDefault());/*@cc_on@if(@_win32)n.cancelBubble=!0,n.returnValue=!1;@end@*/return!1},s=function(n){n=n||window.event;if(n.preventDefault){n.preventDefault();return}n.returnValue=!1},f=function(n,t){if(new RegExp("(^|\\s)"+t+"(\\s|$)").test(n.className))return;n.className+=(n.className?" ":"")+t},u=function(n,t){n.className=t?n.className.replace(new RegExp("(^|\\s)"+t+"(\\s|$)")," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""):""},ct=function(){var i={},t;for(t in n)i[t]=n[t].getValueSet();return i},st=function(t,i){n[t].setValueSet(!!i)},e=function(t){return!!(t in n&&n.hasOwnProperty(t))},vt=function(t){if(!t||!t.inp||!t.inp.tagName||t.inp.tagName.search(/^input|select/i)==-1)return!1;t.html5Shim=!1;if(t.inp.tagName.toLowerCase()=="select"){if(t.inp.options.length<2)return!1;t.min=0,t.max=t.inp.options.length-1,t.step=1,t.precision=0,t.scale=!1,t.forceValue=!0}else{if(String(t.inp.type).search(/^(text|range)$/i)==-1)return!1;t.min=t.min&&String(t.min).search(v)!=-1?+t.min:0,t.max=t.max&&String(t.max).search(v)!=-1?+t.max:100,t.step=t.step&&String(t.step).search(b)!=-1?t.step:1,t.precision=t.precision&&String(t.precision).search(/^[0-9]+$/)!=-1?t.precision:String(t.step).search(/\.([0-9]+)$/)!=-1?String(t.step).match(/\.([0-9]+)$/)[1].length:0,t.scale=t.scale||!1,t.forceValue="forceValue"in t?!!t.forceValue:!1,t.userSnap="userSnap"in t?!!t.userSnap:!1}return t.ariaFormat="ariaFormat"in t&&typeof t.ariaFormat=="function"?t.ariaFormat:!1,t.maxStep=t.maxStep&&String(t.maxStep).search(b)!=-1?+t.maxStep:+t.step*2,t.classNames=t.classNames||"",t.callbacks=t.callbacks||!1,a(t.inp.id),n[t.inp.id]=new ut(t),!0},r=function(n,t){return n.getAttribute(t)||""},l=function(){for(var e=document.getElementsByTagName("input"),i,u,t,f=0;t=e[f];f++)if(t.tagName.toLowerCase()=="input"&&r(t,"type")&&r(t,"type").toLowerCase()=="range"&&(r(t,"min")&&r(t,"min").search(v)!=-1||r(t,"max")&&r(t,"max").search(v)!=-1||r(t,"step")&&r(t,"step").search(/^(any|([0-9]+(\.[0-9]+){0,1}))$/i)!=-1)){if(t.id&&document.getElementById("fd-slider-"+t.id))continue;else t.id&&!document.getElementById("fd-slider-"+t.id)&&a(t.id);t.id||(t.id="fd-slider-form-elem-"+lt++),i={inp:t,callbacks:[],animation:ft,vertical:r(t,"data-fd-slider-vertical")?!0:t.offsetHeight>t.offsetWidth,classNames:r(t,"data-fd-slider-vertical"),html5Shim:!0},i.vertical&&!r(t,"data-fd-slider-vertical")&&(i.inpHeight=t.offsetHeight),u=g(t),i.min=u.min,i.max=u.max,i.step=u.step,i.precision=String(i.step).search(/\.([0-9]+)$/)!=-1?String(i.step).match(/\.([0-9]+)$/)[1].length:0,i.maxStep=i.step*2,a(i.inp.id),n[i.inp.id]=new ut(i)}return!0},g=function(n){return{min:+(r(n,"min")||0),max:+(r(n,"max")||100),step:+(r(n,"step").search(b)!=-1?n.getAttribute("step"):1)}},a=function(t){return t in n&&n.hasOwnProperty(t)?(n[t].destroy(),delete n[t],!0):!1},tt=function(){for(var i in n)n.hasOwnProperty(i)&&n[i].destroy();n=[]},at=function(){tt(),n=null},d=function(){for(var i in n)n.hasOwnProperty(i)&&n[i].onResize()},et=function(){for(var t in n)n.hasOwnProperty(t)&&n[t].rescan()},ht=function(){nt(),l()},nt=function(){i(window,"load",l)};t(window,"load",l),t(window,"load",function(){setTimeout(function(){var t;for(t in n)n[t].checkValue()},0)}),t(window,"resize",d),t(window,"unload",at),(function(){var t=document.getElementsByTagName("script"),n=ot(String(t[t.length-1].innerHTML).replace(/[\n\r\s\t]+/g," ").replace(/^\s+/,"").replace(/\s+$/,""));typeof n!="object"||"err"in n||it(n)})();/*@if(@_jscript_version<9)f(document.documentElement,"oldie");@end@*/return{rescanDocument:l,createSlider:function(n){return vt(n)},onDomReady:function(){ht()},destroyAll:function(){tt()},destroySlider:function(n){return a(n)},redrawAll:function(){d()},addEvent:t,removeEvent:i,stopEvent:k,increment:function(t,i){if(!e(t))return!1;n[t].increment(i)},stepUp:function(t,i){if(!e(t))return!1;n[t].stepUp(Math.abs(i)||1)},stepDown:function(t,i){if(!e(t))return!1;n[t].stepDown(-Math.abs(i)||-1)},setRange:function(t,i,r){if(!e(t))return!1;n[t].setRange(i,r)},updateSlider:function(t){if(!e(t))return!1;n[t].onResize(),n[t].reset()},disable:function(t){if(!e(t))return!1;n[t].disable()},enable:function(t){if(!e(t))return!1;n[t].enable()},getValueSet:function(){return ct()},setValueSet:function(n,t){if(!e(id))return!1;st(n,t)},setGlobalVariables:function(n){it(n)},removeOnload:function(){nt()},rescanAttributes:et}}()
\ No newline at end of file
index 9644e6f2a634a6d4f00031628e3aabacae70ec83..289060de129944574f821f8b3c162753b138f658 100644 (file)
@@ -144,7 +144,7 @@ body
   50%
     fill #ff9d00
 
-.header-content .estop
+.estop
   width 130px
   transition 250ms
 
@@ -369,8 +369,14 @@ body
     > svg
       margin 1em
 
-  .console
+.console
+  table
     width 100%
+    margin 0.5em 0
+    border-collapse collapse
+
+    td, th
+      border 1px solid #ddd
 
     tr
       > td
@@ -386,48 +392,48 @@ body
       &.debug td
         color green
 
-  .indicators
-    table
-      display inline-block
-      vertical-align top
-      margin 0.5em
-      empty-cells show
+.indicators
+  table
+    display inline-block
+    vertical-align top
+    margin 0.5em
+    empty-cells show
 
-      td, th
-        padding 4px
+    td, th
+      padding 4px
 
-      tr:nth-child(odd)
-        background #f7f7f7
+    tr:nth-child(odd)
+      background #f7f7f7
 
-      td:nth-child(1), td:nth-child(3)
-        text-align center
+    td:nth-child(1), td:nth-child(3)
+      text-align center
 
-      th:nth-child(2), th:nth-child(4)
-        text-align left
+    th:nth-child(2), th:nth-child(4)
+      text-align left
 
-      &.inputs, &.outputs
-        td:nth-child(1), td:nth-child(4)
-          text-align center
+    &.inputs, &.outputs
+      td:nth-child(1), td:nth-child(4)
+        text-align center
 
-        th:nth-child(2), th:nth-child(5)
-          text-align right
+      th:nth-child(2), th:nth-child(5)
+        text-align right
 
-        th:nth-child(3), th:nth-child(6)
-          text-align left
+      th:nth-child(3), th:nth-child(6)
+        text-align left
 
-    .logic-lo
-      color black
+  .logic-lo
+    color black
 
-    .logic-hi
-      color green
+  .logic-hi
+    color green
 
-    .warn
-      background-color transparent
-      color orange
+  .warn
+    background-color transparent
+    color orange
 
-  .video
-    text-align center
-    line-height 0
+.video
+  text-align center
+  line-height 0
 
 
 .tabs
@@ -479,6 +485,19 @@ body
     border 1px solid #ddd
     padding 0.5em
 
+
+.upgrade-version
+  display inline-block
+  border-radius 4px
+  padding 2px
+  margin-left 0.5em
+  color #555
+  background-color #e5aa3d
+  text-decoration none
+
+  &:hover
+    color #fff
+
 .modal-mask
   position fixed
   z-index 9998
@@ -527,6 +546,22 @@ label.file-upload
     position fixed
     top -1000px
 
+.error-message
+  &.modal-mask .modal-wrapper .modal-container
+    width auto
+    max-width 800px
+
+  .modal-header h3
+    white-space nowrap
+    overflow hidden
+    text-overflow ellipsis
+
+  .estop
+    float right
+    transform scale(0.75)
+    margin-top -30px
+
+
 @media only screen and (max-width 48em)
   .header
     height auto