diff --git a/CHANGELOG.md b/CHANGELOG.md
index 420e6f2..58a0495 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +1,16 @@
# Change Log
+
+2018/05/30
+==========
+
+Improved compatibility with the W3C specification (https://w3c.github.io/speech-api/webspeechapi.html) and other improvements.
+* Added `voicechanged` event.
+* Error events from Android now include the `error` value and message.
+* Removed SpeechSynthesisVoiceList.
+
+2019/03/13
+==========
+
+Added iOS implementation. Apple's implementation of speechSynthesis
+crashes in heavy use. In particular, crashes were seen when used with
+wifisher's phonegap-plugin-speechsynthesis plugin.
diff --git a/README.md b/README.md
index bf324b1..197b4ca 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,33 @@
# SpeechSynthesisPlugin
-W3C Web Speech API - Speech synthesis plugin for PhoneGap
+W3C Web Speech API - Speech synthesis plugin for Cordova/PhoneGap.
+
+This Cordova plugin provides speech synthesis support for Android, Apple iOS, Windows 10, and the Browser.
+It attempts to closely follow the
+[SpeechSynthesis](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis)
+interface as defined by the
+[Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API).
+
+This plugin provides a native implementation for Android and iOS.
+For Windows 10, and Browser, the native webview implementation is used.
# Installation
-## Phone Gap
+## Cordova
Using the command line tools run:
- phonegap plugin add https://github.com/macdonst/SpeechSynthesisPlugin
- cordova plugin add https://github.com/macdonst/SpeechSynthesisPlugin
+ cordova plugin add https://github.com/wifisher/SpeechSynthesisPlugin
-## Cordova
+# Cordova Usage
-This plugin also works with the Apache Cordova toolset. See this Github project for an example for Android:
+This plugin works with the Apache Cordova toolset. See this Github project for an old example for Android:
https://github.com/andysylvester/talk-to-me-cordova
More info on using this plugin with Cordova is available at this blog post.
-
-# Example Code
-
+## Example Code
This code from the above Github project shows how to read the value of a text field, set up the plugin to speak that text, and vibrate the phone for 2 seconds:
@@ -32,7 +38,7 @@ This code from the above Github project shows how to read the value of a text fi
txt = x.elements[0].value
u.text = txt;
u.lang = 'en-US';
- speechSynthesis.speak(u);
+ speechSynthesis.speak(u);
navigator.notification.vibrate(2000);
document.getElementById("frm1").reset();
}
diff --git a/package-lock.json b/package-lock.json
index ef4ec3c..f5a5874 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,94 +1,1291 @@
{
"name": "phonegap-plugin-speech-synthesis",
- "version": "0.1.0",
+ "version": "0.2.2",
"lockfileVersion": 1,
+ "requires": true,
"dependencies": {
- "coffee-script": {
- "version": "1.12.6",
- "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.6.tgz",
- "integrity": "sha1-KFo/cRVokGUGTWv570Vy22ZpXL8="
+ "ansi-align": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",
+ "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=",
+ "requires": {
+ "string-width": "^1.0.1"
+ }
},
- "fileset": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.8.tgz",
- "integrity": "sha1-UGuRqTluqn4y+0KoQHfHoMc2t0E="
+ "ansi-escapes": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+ "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
},
- "gaze": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz",
- "integrity": "sha1-X5S92gr+U7xxCWm81vKCVI1gwnk="
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
- "glob": {
- "version": "3.2.11",
- "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
- "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "any-observable": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.2.0.tgz",
+ "integrity": "sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI="
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "boxen": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz",
+ "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=",
+ "requires": {
+ "ansi-align": "^1.1.0",
+ "camelcase": "^2.1.0",
+ "chalk": "^1.1.1",
+ "cli-boxes": "^1.0.0",
+ "filled-array": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "repeating": "^2.0.0",
+ "string-width": "^1.0.1",
+ "widest-line": "^1.0.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "capture-stack-trace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
+ "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw=="
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "cli-boxes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+ },
+ "cli-cursor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+ "requires": {
+ "restore-cursor": "^1.0.1"
+ }
+ },
+ "cli-spinners": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz",
+ "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw="
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "coffeescript": {
+ "version": "1.12.7",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz",
+ "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA=="
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "configstore": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz",
+ "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=",
+ "requires": {
+ "dot-prop": "^3.0.0",
+ "graceful-fs": "^4.1.2",
+ "mkdirp": "^0.5.0",
+ "object-assign": "^4.0.1",
+ "os-tmpdir": "^1.0.0",
+ "osenv": "^0.1.0",
+ "uuid": "^2.0.1",
+ "write-file-atomic": "^1.1.2",
+ "xdg-basedir": "^2.0.0"
+ },
"dependencies": {
- "minimatch": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
- "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0="
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "requires": {
+ "minimist": "^1.2.5"
+ }
}
}
},
- "growl": {
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
+ "create-error-class": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+ "requires": {
+ "capture-stack-trace": "^1.0.0"
+ }
+ },
+ "cross-spawn-async": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz",
+ "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=",
+ "requires": {
+ "lru-cache": "^4.0.0",
+ "which": "^1.2.8"
+ }
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "dateformat": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
+ "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "dot-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
+ "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
+ "requires": {
+ "is-obj": "^1.0.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.0.2"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "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="
+ },
+ "execa": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.4.0.tgz",
+ "integrity": "sha1-TrZGejaglfq7KXD/nV4/t7zm68M=",
+ "requires": {
+ "cross-spawn-async": "^2.1.1",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "path-key": "^1.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "exit-hook": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+ "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g="
+ },
+ "figures": {
"version": "1.7.0",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz",
- "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto="
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "filled-array": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz",
+ "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q="
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "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=="
+ },
+ "gaze": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+ "requires": {
+ "globule": "^1.0.0"
+ }
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
+ },
+ "glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "globule": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz",
+ "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==",
+ "requires": {
+ "glob": "~7.1.1",
+ "lodash": "~4.17.10",
+ "minimatch": "~3.0.2"
+ }
+ },
+ "got": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
+ "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
+ "requires": {
+ "create-error-class": "^3.0.1",
+ "duplexer2": "^0.1.4",
+ "is-redirect": "^1.0.0",
+ "is-retry-allowed": "^1.0.0",
+ "is-stream": "^1.0.0",
+ "lowercase-keys": "^1.0.0",
+ "node-status-codes": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "parse-json": "^2.1.0",
+ "pinkie-promise": "^2.0.0",
+ "read-all-stream": "^3.0.0",
+ "readable-stream": "^2.0.5",
+ "timed-out": "^3.0.0",
+ "unzip-response": "^1.0.2",
+ "url-parse-lax": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.9",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
},
"inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
+ "is-core-module": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+ "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-finite": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+ "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-npm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+ "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0="
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-redirect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+ "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+ },
+ "is-retry-allowed": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
+ "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"jasmine-growl-reporter": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-0.0.3.tgz",
- "integrity": "sha1-uHrlUeNZ0orVIXdl6u9sB7dj9sg="
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-2.0.0.tgz",
+ "integrity": "sha512-RYwVfPaGgxQQSHDOt6jQ99/KAkFQ/Fiwg/AzBS+uO9A4UhGhxb7hwXaUUSU/Zs0MxBoFNqmIRC+7P4/+5O3lXg==",
+ "requires": {
+ "growl": "^1.10.5"
+ }
},
"jasmine-node": {
- "version": "1.14.5",
- "resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-1.14.5.tgz",
- "integrity": "sha1-GOg5e4VpJO53ADZmw3MbWupQw50="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-3.0.0.tgz",
+ "integrity": "sha512-vUa5Q7bQYwHHqi6FlJYndiKqZp+d+c3MKe0QUMwwrC4JRmoRV3zkg0buxB/uQ6qLh0NO34TNstpAnvaZ6xGlAA==",
+ "requires": {
+ "coffeescript": "~1.12.7",
+ "gaze": "~1.1.2",
+ "jasmine-growl-reporter": "~2.0.0",
+ "jasmine-reporters": "~1.0.0",
+ "mkdirp": "~0.3.5",
+ "requirejs": "~2.3.6",
+ "underscore": "~1.9.1",
+ "walkdir": "~0.0.12"
+ }
},
"jasmine-reporters": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-1.0.2.tgz",
- "integrity": "sha1-q2E+1Zd9x0h+hbPBL2qOqNsq3jE="
+ "integrity": "sha1-q2E+1Zd9x0h+hbPBL2qOqNsq3jE=",
+ "requires": {
+ "mkdirp": "~0.3.5"
+ }
+ },
+ "latest-version": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
+ "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=",
+ "requires": {
+ "package-json": "^2.0.0"
+ }
+ },
+ "lazy-req": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz",
+ "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w="
+ },
+ "listr": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/listr/-/listr-0.4.3.tgz",
+ "integrity": "sha1-IwLLJNKvAqrYSNEzEZy6//q50j0=",
+ "requires": {
+ "chalk": "^1.1.3",
+ "figures": "^1.7.0",
+ "indent-string": "^2.1.0",
+ "is-stream": "^1.1.0",
+ "log-symbols": "^1.0.2",
+ "log-update": "^1.0.2",
+ "ora": "^0.2.3",
+ "stream-to-observable": "^0.1.0",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "stream-to-observable": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz",
+ "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4="
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "log-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
+ "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
+ "requires": {
+ "chalk": "^1.0.0"
+ }
+ },
+ "log-update": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz",
+ "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=",
+ "requires": {
+ "ansi-escapes": "^1.0.0",
+ "cli-cursor": "^1.0.2"
+ }
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "lowercase-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
},
"lru-cache": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
- "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ }
},
"minimatch": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
- "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo="
+ "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.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
},
+ "node-status-codes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
+ "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8="
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "npm-run-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz",
+ "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=",
+ "requires": {
+ "path-key": "^1.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
+ },
+ "ora": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz",
+ "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=",
+ "requires": {
+ "chalk": "^1.1.1",
+ "cli-cursor": "^1.0.2",
+ "cli-spinners": "^0.1.2",
+ "object-assign": "^4.0.1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "package-json": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz",
+ "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=",
+ "requires": {
+ "got": "^5.0.0",
+ "registry-auth-token": "^3.0.1",
+ "registry-url": "^3.0.3",
+ "semver": "^5.1.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "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-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+ },
+ "path-key": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz",
+ "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68="
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pluginpub": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/pluginpub/-/pluginpub-0.0.9.tgz",
+ "integrity": "sha512-ulgYcVIUnPcFY9bsaIlHd8G1rRTwzq4DldR2H5etnM8Gzf2JB151yT9MWqIs+9LI3ad/746HrRKnrLzE5jYLNA==",
+ "requires": {
+ "any-observable": "^0.2.0",
+ "dateformat": "^2.0.0",
+ "del": "^2.2.0",
+ "execa": "^0.4.0",
+ "listr": "^0.4.3",
+ "meow": "^3.7.0",
+ "pify": "^2.3.0",
+ "rxjs": "^5.0.0-beta.9",
+ "semver": "^5.1.0",
+ "split": "^1.0.0",
+ "stream-to-observable": "^0.2.0",
+ "update-notifier": "^1.0.1",
+ "xml2js": "^0.4.17"
+ }
+ },
+ "prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ }
+ },
+ "read-all-stream": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz",
+ "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=",
+ "requires": {
+ "pinkie-promise": "^2.0.0",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "registry-auth-token": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
+ "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
+ "requires": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+ "requires": {
+ "rc": "^1.0.1"
+ }
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
"requirejs": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.3.tgz",
- "integrity": "sha1-qln9OgKH6vQHlZoTgigES13WpqM="
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+ "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg=="
+ },
+ "resolve": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
+ "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
+ "requires": {
+ "is-core-module": "^2.8.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+ "requires": {
+ "exit-hook": "^1.0.0",
+ "onetime": "^1.0.0"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rxjs": {
+ "version": "5.5.12",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz",
+ "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==",
+ "requires": {
+ "symbol-observable": "1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ },
+ "semver-diff": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+ "requires": {
+ "semver": "^5.0.3"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
+ "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
+ },
+ "slide": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
},
- "sigmund": {
+ "spdx-license-ids": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
+ "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="
+ },
+ "split": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
- "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "requires": {
+ "through": "2"
+ }
+ },
+ "stream-to-observable": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.2.0.tgz",
+ "integrity": "sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA=",
+ "requires": {
+ "any-observable": "^0.2.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+ },
+ "symbol-observable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+ "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "timed-out": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz",
+ "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc="
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
},
"underscore": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
- "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz",
+ "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ=="
+ },
+ "unzip-response": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
+ "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4="
+ },
+ "update-notifier": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz",
+ "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=",
+ "requires": {
+ "boxen": "^0.6.0",
+ "chalk": "^1.0.0",
+ "configstore": "^2.0.0",
+ "is-npm": "^1.0.0",
+ "latest-version": "^2.0.0",
+ "lazy-req": "^1.1.0",
+ "semver-diff": "^2.0.0",
+ "xdg-basedir": "^2.0.0"
+ }
+ },
+ "url-parse-lax": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+ "requires": {
+ "prepend-http": "^1.0.1"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "uuid": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+ "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
},
"walkdir": {
- "version": "0.0.11",
- "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz",
- "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI="
+ "version": "0.0.12",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.12.tgz",
+ "integrity": "sha512-HFhaD4mMWPzFSqhpyDG48KDdrjfn409YQuVW7ckZYhW4sE87mYtWifdB/+73RA7+p4s4K18n5Jfx1kHthE1gBw=="
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "widest-line": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz",
+ "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=",
+ "requires": {
+ "string-width": "^1.0.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write-file-atomic": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
+ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "slide": "^1.1.5"
+ }
+ },
+ "xdg-basedir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz",
+ "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=",
+ "requires": {
+ "os-homedir": "^1.0.0"
+ }
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
}
diff --git a/package.json b/package.json
index c97630d..9d4ce36 100644
--- a/package.json
+++ b/package.json
@@ -1,23 +1,24 @@
{
"name": "phonegap-plugin-speech-synthesis",
- "version": "0.1.1",
+ "version": "0.2.2",
"description": "W3C Web Speech API - Speech synthesis plugin for PhoneGap",
- "homepage": "https://github.com/macdonst/SpeechSynthesisPlugin#readme",
+ "homepage": "https://github.com/wifisher/SpeechSynthesisPlugin#readme",
"scripts": {
"test": "jasmine-node --color spec"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/macdonst/SpeechSynthesisPlugin.git"
+ "url": "git+https://github.com/wifisher/SpeechSynthesisPlugin.git"
},
"bugs": {
- "url": "https://github.com/macdonst/SpeechSynthesisPlugin/issues"
+ "url": "https://github.com/wifisher/SpeechSynthesisPlugin/issues"
},
"cordova": {
"id": "phonegap-plugin-speech-synthesis",
"platforms": [
"ios",
"android",
+ "windows",
"browser"
]
},
@@ -26,12 +27,17 @@
"ecosystem:phonegap",
"cordova-ios",
"cordova-android",
+ "cordova-windows",
"cordova-browser"
],
"author": "Simon MacDonald",
+ "contributors": [
+ "Wayne Fisher
-
+
SpeechSynthesis
Cordova Speech Synthesis Plugin
MIT
@@ -10,6 +10,13 @@
+
+
+
+
+
+
+
@@ -20,11 +27,37 @@
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/android/SpeechSynthesis.java b/src/android/SpeechSynthesis.java
index cfa0406..c6ff97d 100644
--- a/src/android/SpeechSynthesis.java
+++ b/src/android/SpeechSynthesis.java
@@ -1,22 +1,22 @@
package org.apache.cordova.speech;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.ArrayList;
-import java.util.AbstractList;
-import java.util.List;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Locale;
import java.util.Set;
+import java.lang.IllegalArgumentException;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.Build;
+import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
-import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+import android.speech.tts.UtteranceProgressListener;
import android.speech.tts.Voice;
import android.util.Log;
@@ -24,9 +24,9 @@
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
-public class SpeechSynthesis extends CordovaPlugin implements OnInitListener, OnUtteranceCompletedListener {
+public class SpeechSynthesis extends CordovaPlugin implements OnInitListener {
- private static final String LOG_TAG = "TTS";
+ private static final String LOG_TAG = "SpeechSynthesis";
private static final int STOPPED = 0;
private static final int INITIALIZING = 1;
private static final int STARTED = 2;
@@ -34,10 +34,11 @@ public class SpeechSynthesis extends CordovaPlugin implements OnInitListener, On
private int state = STOPPED;
private CallbackContext startupCallbackContext;
private CallbackContext callbackContext;
-
+ private Hashtable contextMap = new Hashtable();
+ private ConcurrentLinkedQueue contextQueue = new ConcurrentLinkedQueue();
private Set voiceList = null;
- //private String startupCallbackId = "";
+ private int androidAPILevel = android.os.Build.VERSION.SDK_INT;
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
@@ -48,61 +49,16 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
try {
if (action.equals("speak")) {
JSONObject utterance = args.getJSONObject(0);
- String text = utterance.getString("text");
-
- String lang = utterance.optString("lang", "en");
- mTts.setLanguage(new Locale(lang));
-
- String voiceCode = utterance.optString("voiceURI", null);
- if (voiceCode == null) {
- JSONObject voice = utterance.optJSONObject("voice");
- if (voice != null) {
- voiceCode = voice.optString("voiceURI", null);
- }
- }
- if (voiceCode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- for (Voice v : this.voiceList) {
- if (voiceCode.equals(v.getName())) {
- mTts.setVoice(v);
- //text+=" yay! found the voice!";
- }
- }
- }
-
- float pitch = (float)utterance.optDouble("pitch", 1.0);
- mTts.setPitch(pitch);
-
- float volume = (float)utterance.optDouble("volume", 0.5);
- // how to set volume
-
- float rate = (float)utterance.optDouble("rate", 1.0);
- mTts.setSpeechRate(rate);
-
- if (isReady()) {
- HashMap map = null;
- map = new HashMap();
- map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackContext.getCallbackId());
- JSONObject event = new JSONObject();
- event.put("type","start");
- event.put("charIndex",0);
- event.put("elapsedTime",0);
- event.put("name","");
- PluginResult pr = new PluginResult(PluginResult.Status.OK, event);
- pr.setKeepCallback(true);
- callbackContext.sendPluginResult(pr);
- mTts.speak(text, TextToSpeech.QUEUE_ADD, map);
- } else {
- fireErrorEvent(callbackContext);
- }
+ speak(callbackContext, utterance, false);
} else if (action.equals("cancel")) {
if (isReady()) {
- HashMap map = null;
- map = new HashMap();
- //map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackId);
- mTts.speak("", TextToSpeech.QUEUE_FLUSH, map);
- fireEndEvent(callbackContext);
+ // Speak an empty message but with flush set.
+ // Hopefully, this will allow for error events to be sent to unfinished utterances.
+ JSONObject utterance = new JSONObject();
+ utterance.put("text", "");
+ speak(callbackContext, utterance, true);
} else {
- fireErrorEvent(callbackContext);
+ fireErrorEvent(callbackContext, 6, "Not ready.");
}
} else if (action.equals("pause")) {
Log.d(LOG_TAG, "Not implemented yet");
@@ -113,7 +69,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
mTts.stop();
callbackContext.sendPluginResult(new PluginResult(status, result));
} else {
- fireErrorEvent(callbackContext);
+ fireErrorEvent(callbackContext, 6, "Not ready.");
}
} else if (action.equals("silence")) {
if (isReady()) {
@@ -125,7 +81,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
pr.setKeepCallback(true);
callbackContext.sendPluginResult(pr);
} else {
- fireErrorEvent(callbackContext);
+ fireErrorEvent(callbackContext, 6, "Not ready.");
}
} else if (action.equals("startup")) {
this.startupCallbackContext = callbackContext;
@@ -139,9 +95,6 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
pluginResult.setKeepCallback(true);
startupCallbackContext.sendPluginResult(pluginResult);
}
-
-
-
else if (action.equals("shutdown")) {
if (mTts != null) {
mTts.shutdown();
@@ -163,6 +116,64 @@ else if (action.equals("isLanguageAvailable")) {
return false;
}
+ private void speak(CallbackContext callbackContext, JSONObject utterance, boolean flush) {
+ String text = null;
+
+ // Track the utterance CallbackContext so that we can later send the events to the right place.
+ contextMap.put(callbackContext.getCallbackId(), callbackContext);
+ contextQueue.add(callbackContext.getCallbackId());
+
+ try {
+ text = utterance.getString("text");
+ } catch (JSONException e) {
+ // this should never happen
+ }
+
+ String lang = utterance.optString("lang", "en");
+ mTts.setLanguage(new Locale(lang));
+
+ String voiceCode = utterance.optString("voiceURI", null);
+ if (voiceCode == null) {
+ JSONObject voice = utterance.optJSONObject("voice");
+ if (voice != null) {
+ voiceCode = voice.optString("voiceURI", null);
+ }
+ }
+ if (voiceCode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ for (Voice v : this.voiceList) {
+ if (voiceCode.equals(v.getName())) {
+ mTts.setVoice(v);
+ //text+=" yay! found the voice!";
+ }
+ }
+ }
+
+ float pitch = (float) utterance.optDouble("pitch", 1.0);
+ mTts.setPitch(pitch);
+
+ float volume = (float) utterance.optDouble("volume", 0.5);
+ // how to set volume
+
+ float rate = (float) utterance.optDouble("rate", 1.0);
+ mTts.setSpeechRate(rate);
+
+ if (isReady()) {
+ Log.d(LOG_TAG, "utterance id: " + callbackContext.getCallbackId() + ", text: " + text);
+ if (androidAPILevel < 21) {
+ HashMap map = new HashMap();
+ map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackContext.getCallbackId());
+ map.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, Float.toString(volume));
+ mTts.speak(text, flush ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, map);
+ } else { // android API level is 21 or higher...
+ Bundle params = new Bundle();
+ params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, volume);
+ mTts.speak(text, flush ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, params, callbackContext.getCallbackId());
+ }
+ } else {
+ fireErrorEvent(callbackContext, 6, "Not ready.");
+ }
+ }
+
private void getVoices(CallbackContext callbackContext) {
JSONArray voices = new JSONArray();
JSONObject voice;
@@ -195,47 +206,81 @@ private void getVoices(CallbackContext callbackContext) {
// locale = list.next();
for (int i = 0; i < list.length; i++) {
locale = list[i];
- voice = new JSONObject();
- if (mTts.isLanguageAvailable(locale) > 0) { // ie LANG_COUNTRY_AVAILABLE or LANG_COUNTRY_VAR_AVAILABLE
- try {
+ try {
+ // PMPA-680: Filter out just English and French since the app doesn't support anything else.
+ if ((locale.getLanguage().equals("en") || locale.getLanguage().equals("fr")) &&
+ mTts.isLanguageAvailable(locale) > 0) { // ie LANG_COUNTRY_AVAILABLE or LANG_COUNTRY_VAR_AVAILABLE
+ voice = new JSONObject();
voice.put("voiceURI", locale.getLanguage()+"-"+locale.getCountry());
voice.put("name", locale.getDisplayLanguage(locale) + " " + locale.getDisplayCountry(locale));
voice.put("lang", locale.getLanguage()+"-"+locale.getCountry());
voice.put("localService", true);
voice.put("default", false);
- } catch (JSONException e) {
- // should never happen
+ voices.put(voice);
}
- voices.put(voice);
+ } catch (JSONException e) {
+ // should never happen
+ } catch (IllegalArgumentException e) {
+ // PMPA-680: See some exceptions here from some Android variants.
+ Log.d(LOG_TAG, "Caught IllegalArgumentException for locale " + locale + " - " + e);
+ } catch (Exception e) {
+ // PMPA-680: See some exceptions here from some Android variants.
+ Log.d(LOG_TAG, "Caught Exception for locale " + locale + " - " + e);
}
}
}
PluginResult result = new PluginResult(PluginResult.Status.OK, voices);
result.setKeepCallback(false);
startupCallbackContext.sendPluginResult(result);
- mTts.setOnUtteranceCompletedListener(this);
}
- private void fireEndEvent(CallbackContext callbackContext) {
+ private void fireEvent(CallbackContext callbackContext, String type) {
JSONObject event = new JSONObject();
+
+ Log.d(LOG_TAG, "fire event: " + type);
+
try {
- event.put("type","end");
+ event.put("type",type);
+ if(type.equals("start")) {
+ event.put("charIndex", 0);
+ event.put("elapsedTime", 0);
+ event.put("name", "");
+ }
} catch (JSONException e) {
- // this will never happen
+ // this should never happen
}
PluginResult pr = new PluginResult(PluginResult.Status.OK, event);
- pr.setKeepCallback(false);
- callbackContext.sendPluginResult(pr);
+ if(type.equals("end")) {
+ pr.setKeepCallback(false);
+ } else {
+ pr.setKeepCallback(true);
+ }
+
+ if(callbackContext == null) {
+ this.callbackContext.sendPluginResult(pr);
+ } else {
+ callbackContext.sendPluginResult(pr);
+ }
}
- private void fireErrorEvent(CallbackContext callbackContext)
- throws JSONException {
+ private void fireErrorEvent(CallbackContext callbackContext, int errCode, String message) {
JSONObject error = new JSONObject();
- error.put("type","error");
- error.put("charIndex",0);
- error.put("elapsedTime",0);
- error.put("name","");
- callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
+ Log.d(LOG_TAG, "fire event: error!");
+ try {
+ error.put("type","error");
+ error.put("charIndex",0);
+ error.put("elapsedTime",0);
+ error.put("name","");
+ error.put("error", errCode);
+ error.put("message", message);
+ } catch (JSONException e) {
+ // this should never happen
+ }
+ if(callbackContext == null) {
+ this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
+ } else {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
+ }
}
/**
@@ -256,36 +301,104 @@ public void onInit(int status) {
if (mTts != null && status == TextToSpeech.SUCCESS) {
state = SpeechSynthesis.STARTED;
getVoices(this.startupCallbackContext);
-
-
-// Putting this code in hear as a place holder. When everything moves to API level 15 or greater
-// we'll switch over to this way of tracking progress.
-// mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
-//
-// @Override
-// public void onDone(String utteranceId) {
-// Log.d(LOG_TAG, "got completed utterance");
-// PluginResult result = new PluginResult(PluginResult.Status.OK);
-// result.setKeepCallback(false);
-// callbackContext.sendPluginResult(result);
-// }
-//
-// @Override
-// public void onError(String utteranceId) {
-// Log.d(LOG_TAG, "got utterance error");
-// PluginResult result = new PluginResult(PluginResult.Status.ERROR);
-// result.setKeepCallback(false);
-// callbackContext.sendPluginResult(result);
-// }
-//
-// @Override
-// public void onStart(String utteranceId) {
-// Log.d(LOG_TAG, "started talking");
-// }
-//
-// });
- }
- else if (status == TextToSpeech.ERROR) {
+
+ mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
+ @Override
+ public void onDone(String utteranceId) {
+ Log.d(LOG_TAG, utteranceId + ": got completed utterance");
+ fireEvent(contextMap.get(utteranceId), "end");
+
+ // No longer need the CallbackContext for this utterance.
+ contextMap.remove(utteranceId);
+ if(utteranceId.equals(contextQueue.peek())) {
+ contextQueue.poll();
+ }
+ }
+
+ @Override
+ public void onError(String utteranceId) {
+ this.onError(utteranceId, -99);
+ }
+
+ @Override
+ public void onError(String utteranceId, int errorCode) {
+ int error;
+ String message;
+
+ Log.d(LOG_TAG, utteranceId + ": got utterance error");
+
+ switch(errorCode) {
+ case TextToSpeech.ERROR:
+ error = 6;
+ message = "Generic operation failure.";
+ break;
+
+ case TextToSpeech.ERROR_INVALID_REQUEST:
+ error = 6;
+ message = "Invalid request.";
+ break;
+
+ case TextToSpeech.ERROR_NETWORK:
+ error = 4;
+ message = "Network connectivity problem.";
+ break;
+
+ case TextToSpeech.ERROR_NETWORK_TIMEOUT:
+ error = 4;
+ message = "Network timeout.";
+ break;
+
+ case TextToSpeech.ERROR_NOT_INSTALLED_YET:
+ error = 5;
+ message = "Unfinished download of the voice data.";
+ break;
+
+ case TextToSpeech.ERROR_OUTPUT:
+ error = 3;
+ message = "Audio output issue.";
+ break;
+
+ case TextToSpeech.ERROR_SERVICE:
+ error = 6;
+ message = "Text-To-Speech failure.";
+ break;
+
+ case TextToSpeech.ERROR_SYNTHESIS:
+ error = 6;
+ message = "Unable to synthesize the text.";
+ break;
+
+ default:
+ error = 6;
+ message = "Unknown error.";
+ break;
+ }
+ fireErrorEvent(contextMap.get(utteranceId), error, message);
+
+ // No longer need the CallbackContext for this utterance.
+ contextMap.remove(utteranceId);
+ if(utteranceId.equals(contextQueue.peek())) {
+ contextQueue.poll();
+ }
+ }
+
+ @Override
+ public void onStart(String utteranceId) {
+ String id;
+
+ Log.d(LOG_TAG, utteranceId + ": started talking");
+ fireEvent(contextMap.get(utteranceId),"start");
+
+ // Any contexts that have not been processed should be flagged with an error.
+ while(((id = contextQueue.peek()) != null) && !utteranceId.equals(id)) {
+ fireErrorEvent(contextMap.get(id), 6, "Lost event.");
+ contextMap.remove(id);
+ contextQueue.poll();
+ }
+ }
+
+ });
+ } else if (status == TextToSpeech.ERROR) {
state = SpeechSynthesis.STOPPED;
PluginResult result = new PluginResult(PluginResult.Status.ERROR, SpeechSynthesis.STOPPED);
result.setKeepCallback(false);
@@ -301,11 +414,4 @@ public void onDestroy() {
mTts.shutdown();
}
}
-
- /**
- * Once the utterance has completely been played call the speak's success callback
- */
- public void onUtteranceCompleted(String utteranceId) {
- fireEndEvent(callbackContext);
- }
}
diff --git a/src/ios/NSMutableArray+QueueAdditions.h b/src/ios/NSMutableArray+QueueAdditions.h
new file mode 100644
index 0000000..7152761
--- /dev/null
+++ b/src/ios/NSMutableArray+QueueAdditions.h
@@ -0,0 +1,15 @@
+/*
+ Generic queue.
+ */
+#import
+
+@interface NSMutableArray (QueueAdditions)
+
+-(id) dequeue;
+-(void) enqueue:(id)obj;
+-(id) peek:(int)index;
+-(id) peekHead;
+-(id) peekTail;
+-(BOOL) empty;
+
+@end
diff --git a/src/ios/NSMutableArray+QueueAdditions.m b/src/ios/NSMutableArray+QueueAdditions.m
new file mode 100644
index 0000000..cdcc065
--- /dev/null
+++ b/src/ios/NSMutableArray+QueueAdditions.m
@@ -0,0 +1,66 @@
+#import "NSMutableArray+QueueAdditions.h"
+
+@implementation NSMutableArray (QueueAdditions)
+
+// Add to the tail of the queue
+-(void) enqueue: (id) anObject {
+ // Push the item in
+ [self addObject: anObject];
+}
+
+// Grab the next item in the queue, if there is one
+-(id) dequeue {
+ // Set aside a reference to the object to pass back
+ id queueObject = nil;
+
+ // Do we have any items?
+ if ([self lastObject]) {
+ // Pick out the first one
+#if !__has_feature(objc_arc)
+ queueObject = [[[self objectAtIndex: 0] retain] autorelease];
+#else
+ queueObject = [self objectAtIndex: 0];
+#endif
+ // Remove it from the queue
+ [self removeObjectAtIndex: 0];
+ }
+
+ // Pass back the dequeued object, if any
+ return queueObject;
+}
+
+// Takes a look at an object at a given location
+-(id) peek: (int) index {
+ // Set aside a reference to the peeked at object
+ id peekObject = nil;
+ // Do we have any items at all?
+ if ([self lastObject]) {
+ // Is this within range?
+ if (index < [self count]) {
+ // Get the object at this index
+ peekObject = [self objectAtIndex: index];
+ }
+ }
+
+ // Pass back the peeked at object, if any
+ return peekObject;
+}
+
+// Let's take a look at the next item to be dequeued
+-(id) peekHead {
+ // Peek at the next item
+ return [self peek: 0];
+}
+
+// Let's take a look at the last item to have been added to the queue
+-(id) peekTail {
+ // Pick out the last item
+ return [self lastObject];
+}
+
+// Checks if the queue is empty
+-(BOOL) empty {
+ return ([self lastObject] == nil);
+}
+
+@end
diff --git a/src/ios/SpeechSynthesis.h b/src/ios/SpeechSynthesis.h
new file mode 100644
index 0000000..5bf4ab8
--- /dev/null
+++ b/src/ios/SpeechSynthesis.h
@@ -0,0 +1,30 @@
+/*
+ Modified for use in the Speech Synthesis plugin by Wayne Fisher.
+ Copyright (c) 2019 Fisherlea Systems.
+
+ Original code from:
+ Cordova Text-to-Speech Plugin
+ https://github.com/vilic/cordova-plugin-tts
+
+ by VILIC VANE
+ https://github.com/vilic
+
+ MIT License
+*/
+
+#import
+#import
+
+@interface SpeechSynthesis : CDVPlugin {
+ AVSpeechSynthesizer* synthesizer;
+ AVAudioSession *audioSession;
+ CDVPluginResult* pluginResult;
+ NSMutableArray* commandQueue;
+}
+
+- (void)speak:(CDVInvokedUrlCommand*)command;
+- (void)cancel:(CDVInvokedUrlCommand*)command;
+- (void)pause:(CDVInvokedUrlCommand*)command;
+- (void)resume:(CDVInvokedUrlCommand*)command;
+- (void)startup:(CDVInvokedUrlCommand*)command;
+@end
diff --git a/src/ios/SpeechSynthesis.m b/src/ios/SpeechSynthesis.m
new file mode 100644
index 0000000..81a9c51
--- /dev/null
+++ b/src/ios/SpeechSynthesis.m
@@ -0,0 +1,271 @@
+/*
+ Modified for use in the Speech Synthesis plugin by Wayne Fisher.
+ Copyright (c) 2019 Fisherlea Systems.
+
+ Original code from:
+ Cordova Text-to-Speech Plugin
+ https://github.com/vilic/cordova-plugin-tts
+
+ by VILIC VANE
+ https://github.com/vilic
+
+ MIT License
+*/
+
+#import
+#import
+#import "SpeechSynthesis.h"
+#import "NSMutableArray+QueueAdditions.h"
+
+#if 0
+#define DBG(a) NSLog(a)
+#define DBG1(a, b) NSLog(a, b)
+#define DBG2(a, b, c) NSLog(a, b, c)
+#else
+#define DBG(a)
+#define DBG1(a, b)
+#define DBG2(a, b, c)
+#endif
+
+@implementation SpeechSynthesis
+
+- (void)pluginInitialize {
+ DBG(@"[ss] pluginInitialize()");
+
+ synthesizer = [AVSpeechSynthesizer new];
+ synthesizer.delegate = self;
+
+ audioSession = [AVAudioSession sharedInstance];
+ commandQueue = [[NSMutableArray alloc] init];
+
+ // Log changes to the audio route.
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
+}
+
+- (void)routeChanged:(NSNotification *)notification {
+ AVAudioSession *audioSession = [AVAudioSession sharedInstance];
+ NSNumber *reason = [notification.userInfo objectForKey:AVAudioSessionRouteChangeReasonKey];
+
+ DBG(@"[ss] routeChanged()");
+
+ AVAudioSessionRouteDescription *route;
+ AVAudioSessionPortDescription *port;
+
+ if ([reason unsignedIntegerValue] == AVAudioSessionRouteChangeReasonNewDeviceAvailable) {
+ NSLog(@"[ss] AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+
+ route = audioSession.currentRoute;
+ port = route.inputs[0];
+ NSLog(@"[ss] New device is %@", port.portType);
+ } else if ([reason unsignedIntegerValue] == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
+ NSLog(@"[ss] AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+
+ route = [notification.userInfo objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
+ port = route.inputs[0];
+ NSLog(@"[ss] Removed device %@", port.portType);
+
+ route = audioSession.currentRoute;
+ port = route.inputs[0];
+ NSLog(@"[ss] Now using device %@", port.portType);
+ } else if ([reason unsignedIntegerValue] == AVAudioSessionRouteChangeReasonCategoryChange) {
+ NSLog(@"[ss] AVAudioSessionRouteChangeReasonCategoryChange");
+
+ AVAudioSessionCategory category = [audioSession category];
+
+ NSLog(@"[ss] AVAudioSession category: %@, categoryOptions = %d",
+ category, (int) audioSession.categoryOptions);
+
+ if(![category isEqualToString:AVAudioSessionCategoryPlayback] &&
+ ![category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) {
+ if([category isEqualToString:AVAudioSessionCategoryRecord]) {
+ category = AVAudioSessionCategoryPlayAndRecord;
+ } else {
+ category = AVAudioSessionCategoryPlayback;
+ }
+
+ [audioSession setCategory:category error:nil];
+ }
+ }
+}
+
+- (void)speak:(CDVInvokedUrlCommand*)command {
+ NSDictionary* options = [command.arguments objectAtIndex:0];
+
+ NSString* text = [options objectForKey:@"text"];
+
+ DBG1(@"[ss] speak(%@)", text);
+ [commandQueue enqueue:command];
+
+ NSString* lang = [options objectForKey:@"lang"];
+ NSDictionary* voice = [options objectForKey:@"voice"];
+ float volume = [[options objectForKey:@"volume"] doubleValue];
+ double rate = [[options objectForKey:@"rate"] doubleValue];
+ double pitch = [[options objectForKey:@"pitch"] doubleValue];
+
+ AVAudioSessionCategory category = [audioSession category];
+ if(![category isEqualToString:AVAudioSessionCategoryPlayback] &&
+ ![category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) {
+ [audioSession setActive:NO withOptions:0 error:nil];
+
+ if([category isEqualToString:AVAudioSessionCategoryRecord]) {
+ category = AVAudioSessionCategoryPlayAndRecord;
+ } else {
+ category = AVAudioSessionCategoryPlayback;
+ }
+
+ NSUInteger options = [audioSession categoryOptions] | AVAudioSessionCategoryOptionDuckOthers;
+ [audioSession setCategory:category withOptions:options error:nil];
+ }
+
+ if (!lang || (id)lang == [NSNull null]) {
+ lang = @"en-US";
+ }
+
+ if (!volume) {
+ volume = 1.0;
+ }
+
+ if (!rate) {
+ rate = 1.0;
+ }
+
+ if (!pitch) {
+ pitch = 1.2;
+ }
+
+ AVSpeechUtterance* utterance = [[AVSpeechUtterance new] initWithString:text];
+ utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:lang];
+ // The rate appears to be exponential. Adjust our linear scale accordingly.
+ // The equation was worked out by mapping iOS rate to actual rate by timing between start and end.
+ utterance.rate = (log(rate) + 1) / 2.14;
+ if(utterance.rate > (double) AVSpeechUtteranceMaximumSpeechRate) {
+ utterance.rate = (double) AVSpeechUtteranceMaximumSpeechRate;
+ }
+ utterance.volume = volume;
+ utterance.pitchMultiplier = pitch;
+
+ if(voice) {
+ NSString *identifier = [voice valueForKey:@"voiceURI"];
+
+ utterance.voice = [AVSpeechSynthesisVoice voiceWithIdentifier:identifier];
+ }
+
+ [synthesizer speakUtterance:utterance];
+}
+
+- (void)cancel:(CDVInvokedUrlCommand*)command {
+ DBG(@"[ss] cancel()");
+
+ [synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
+ // TODO: Need to dequeue and send cancels to any queued commands
+}
+
+- (void)pause:(CDVInvokedUrlCommand*)command {
+ DBG(@"[ss] pause()");
+
+ [synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
+ // TODO: Need to send pause event
+}
+
+- (void)resume:(CDVInvokedUrlCommand*)command {
+ DBG(@"[ss] resume()");
+
+ if(synthesizer.paused) {
+ [synthesizer continueSpeaking];
+ // TODO: Need to send resume event
+ }
+}
+
+- (void)startup:(CDVInvokedUrlCommand *)command {
+ DBG(@"[ss] startup()");
+
+ NSMutableArray* list = [[NSMutableArray alloc] init];
+ NSArray *voices = [AVSpeechSynthesisVoice speechVoices];
+
+ DBG1(@"Number of voices: %d", (int) voices.count);
+
+ for (id voiceName in voices) {
+ NSMutableDictionary * voiceDict = [[NSMutableDictionary alloc] init];
+ [voiceDict setValue:[voiceName valueForKey:@"identifier"] forKey:@"voiceURI"];
+ [voiceDict setValue:[voiceName valueForKey:@"name"] forKey:@"name"];
+ [voiceDict setValue:[voiceName valueForKey:@"language"] forKey:@"lang"];
+ [voiceDict setValue:[NSNumber numberWithBool:true] forKey:@"localService"];
+ [voiceDict setValue:[NSNumber numberWithBool:false] forKey:@"default"];
+
+ [list addObject:voiceDict];
+ }
+
+ DBG1(@"Number of voices in list: %d", (int) list.count);
+
+ CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:list];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+-(void) sendEvent:(NSString *) eventType
+{
+ NSMutableDictionary * event = [[NSMutableDictionary alloc]init];
+ DBG1(@"[ss] sendEvent(%@)", eventType);
+ [event setValue:eventType forKey:@"type"];
+ [event setValue:@"" forKey:@"name"];
+ [event setValue:0 forKey:@"charIndex"];
+ [event setValue:0 forKey:@"elapsedTime"];
+
+ CDVInvokedUrlCommand *command = [commandQueue peekHead];
+ if(command) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:event];
+ if(![eventType isEqualToString:@"end"]) {
+ [pluginResult setKeepCallbackAsBool:YES];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+ DBG1(@"[ss] sendEvent(%@) complete", eventType);
+}
+
+-(void) sendErrorEvent:(NSString *) errorCode
+{
+ NSMutableDictionary * event = [[NSMutableDictionary alloc]init];
+ DBG1(@"[ss] sendErrorEvent(%@)", errorCode);
+ [event setValue:@"error" forKey:@"type"];
+ [event setValue:errorCode forKey:@"error"];
+
+ CDVInvokedUrlCommand *command = [commandQueue peekHead];
+ if(command) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:event];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+ DBG1(@"[ss] sendErrorEvent(%@) complete", errorCode);
+}
+
+- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didStartSpeechUtterance:(AVSpeechUtterance*)utterance {
+ DBG1(@"[ss] didStartSpeechUtterance(%@)", utterance.speechString);
+
+ [self sendEvent:@"start"];
+}
+
+- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance*)utterance {
+ DBG1(@"[ss] didFinishSpeechUtterance(%@)", utterance.speechString);
+
+ [self sendEvent:@"end"];
+ [commandQueue dequeue];
+}
+
+- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance*)utterance {
+ DBG1(@"[ss] didPauseSpeechUtterance(%@)", utterance.speechString);
+
+ [self sendEvent:@"pause"];
+}
+
+- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance*)utterance {
+ DBG1(@"[ss] didContinueSpeechUtterance(%@)", utterance.speechString);
+
+ [self sendEvent:@"resume"];
+}
+
+- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance*)utterance {
+ DBG1(@"[ss] didFinishSpeechUtterance(%@)", utterance.speechString);
+
+ [self sendEvent:@"end"];
+ [commandQueue dequeue];
+}
+
+@end
diff --git a/www/SpeechSynthesis.js b/www/SpeechSynthesis.js
index dde4398..8d1941d 100644
--- a/www/SpeechSynthesis.js
+++ b/www/SpeechSynthesis.js
@@ -1,15 +1,22 @@
var exec = require("cordova/exec");
-var SpeechSynthesisVoiceList = require("./SpeechSynthesisVoiceList");
var SpeechSynthesis = function() {
this.pending = false;
this.speaking = false;
this.paused = false;
- this._voices = null;
+ this._voices = [];
+ this.onvoiceschanged = null;
+
var that = this;
- var successCallback = function(data) {
- that._voices = new SpeechSynthesisVoiceList(data);
+ var successCallback = function (data) {
+ if (Array.isArray(data)) {
+ that._voices = data;
+
+ if (that._voices.length && typeof that.onvoiceschanged === "function") {
+ that.onvoiceschanged({ type: "voiceschanged" });
+ }
+ }
};
exec(successCallback, null, "SpeechSynthesis", "startup", []);
};
@@ -30,9 +37,17 @@ SpeechSynthesis.prototype.speak = function(utterance) {
utterance.onboundry(event);
}
};
- var errorCallback = function() {
+ var errorCallback = function(err) {
if (typeof utterance.onerror === "function") {
- utterance.onerror();
+ var error = new SpeechSynthesisErrorEvent();
+
+ error.error = SpeechSynthesisErrorEvent._errorCodes[err.error];
+ error.message = err.message;
+ error.charIndex = err.charIndex;
+ error.elapsedTime = err.elapsedTime;
+ error.name = err.name;
+
+ utterance.onerror(error);
}
};
diff --git a/www/SpeechSynthesisErrorEvent.js b/www/SpeechSynthesisErrorEvent.js
new file mode 100644
index 0000000..b4d294d
--- /dev/null
+++ b/www/SpeechSynthesisErrorEvent.js
@@ -0,0 +1,38 @@
+var SpeechSynthesisErrorEvent = function () {
+ SpeechSynthesisEvent.call(this);
+
+ this.type = "error";
+ this.error = null;
+ this.message = null;
+};
+
+SpeechSynthesisErrorEvent.prototype = new SpeechSynthesisEvent;
+SpeechSynthesisErrorEvent.prototype.constructor = SpeechSynthesisErrorEvent;
+
+SpeechSynthesisErrorEvent['canceled'] = 0;
+SpeechSynthesisErrorEvent['interrupted'] = 1;
+SpeechSynthesisErrorEvent['audio-busy'] = 2;
+SpeechSynthesisErrorEvent['audio-hardware'] = 3;
+SpeechSynthesisErrorEvent['network'] = 4;
+SpeechSynthesisErrorEvent['synthesis-unavailable'] = 5;
+SpeechSynthesisErrorEvent['synthesis-failed'] = 6;
+SpeechSynthesisErrorEvent['language-unavailable'] = 7;
+SpeechSynthesisErrorEvent['voice-unavailable'] = 8;
+SpeechSynthesisErrorEvent['text-too-long'] = 9;
+SpeechSynthesisErrorEvent['invalid-argument'] = 10;
+
+SpeechSynthesisErrorEvent._errorCodes = [
+ 'canceled',
+ 'interrupted',
+ 'audio-busy',
+ 'audio-hardware',
+ 'network',
+ 'synthesis-unavailable',
+ 'synthesis-failed',
+ 'language-unavailable',
+ 'voice-unavailable',
+ 'text-too-long',
+ 'invalid-argument'
+];
+
+module.exports = SpeechSynthesisErrorEvent;
diff --git a/www/SpeechSynthesisEvent.js b/www/SpeechSynthesisEvent.js
index fb2c7d5..5c277ea 100644
--- a/www/SpeechSynthesisEvent.js
+++ b/www/SpeechSynthesisEvent.js
@@ -1,7 +1,8 @@
-var SpeechSynthesisEvent = function() {
- this.charIndex;
- this.elapsedTime;
- this.name;
+var SpeechSynthesisEvent = function () {
+ this.type = "";
+ this.charIndex = 0;
+ this.elapsedTime = 0;
+ this.name = null;
};
module.exports = SpeechSynthesisEvent;
diff --git a/www/SpeechSynthesisUtterance.js b/www/SpeechSynthesisUtterance.js
index e44eabe..3c777ff 100644
--- a/www/SpeechSynthesisUtterance.js
+++ b/www/SpeechSynthesisUtterance.js
@@ -1,7 +1,7 @@
var SpeechSynthesisUtterance = function(text) {
this.text = text;
this.lang;
- this.voiceURI;
+ this.voice;
this.volume;
this.rate;
this.pitch;
diff --git a/www/SpeechSynthesisVoice.js b/www/SpeechSynthesisVoice.js
index eb51943..e4693bf 100644
--- a/www/SpeechSynthesisVoice.js
+++ b/www/SpeechSynthesisVoice.js
@@ -4,7 +4,7 @@ var SpeechSynthesisVoice = function() {
this.name;
this.lang;
this.localService;
- this._default;
+ this.default;
};
-module.exports = SpeechSynthesisVoice;
\ No newline at end of file
+module.exports = SpeechSynthesisVoice;
diff --git a/www/SpeechSynthesisVoiceList.js b/www/SpeechSynthesisVoiceList.js
deleted file mode 100644
index b6e208d..0000000
--- a/www/SpeechSynthesisVoiceList.js
+++ /dev/null
@@ -1,11 +0,0 @@
-
-var SpeechSynthesisVoiceList = function(data) {
- this._list = data;
- this.length = this._list.length;
-};
-
-SpeechSynthesisVoiceList.prototype.item = function(item) {
- return this._list[item];
-};
-
-module.exports = SpeechSynthesisVoiceList;
\ No newline at end of file