diff --git a/.esdoc.json b/.esdoc.json new file mode 100644 index 00000000..90752511 --- /dev/null +++ b/.esdoc.json @@ -0,0 +1,10 @@ +{ + "source": "./src", + "destination": "./docs", + "plugins": [{ + "name": "esdoc-standard-plugin", + "option": { + "accessor": {"access": ["public"], "autoPrivate": true} + } + }] +} diff --git a/.gitignore b/.gitignore index 1173b99b..d784cd40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules bower_components +docs /y.* /examples/yjs-dist.js* diff --git a/documentation.yml b/documentation.yml new file mode 100644 index 00000000..925badf2 --- /dev/null +++ b/documentation.yml @@ -0,0 +1,43 @@ +toc: + - Y + - name: Shared Types + description: | + Types provide an abstraction over the shared data. + Shared types can be edited concurrently by peers. + - Type + - YArray + - YMap + - YText + - YXmlElement + - YXmlFragment + - YXmlHook + - YXmlText + - name: Bindings + description: | + A binding handles data binding from a Yjs type to + a data object + - Binding + - DomBinding + - QuillBinding + - TextareaBinding + - name: Events + description: | + Events describe changes on shared types. + - YArrayEvent + - YEvent + - YMapEvent + - YTextEvent + - YXmlEvent + - name: Binary Encoding + description: | + Yjs efficiently encodes the Yjs model to a binary format. + This section describes utility functions for binary encoding and decoding. + - BinaryEncoder + - BinaryDecoder + - toBinary + - fromBinary + - name: Relative Position + - RelativePosition + - fromRelativePosition + - getRelativePosition + - name: Utility diff --git a/package-lock.json b/package-lock.json index c8facecd..cdd94b4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "abab": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", + "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", + "optional": true + }, "accepts": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", @@ -20,6 +26,23 @@ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true }, + "acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "optional": true, + "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=", + "optional": true + } + } + }, "acorn-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", @@ -73,14 +96,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { "version": "1.3.0", @@ -180,6 +201,17 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "optional": true + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, "async-array-reduce": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/async-array-reduce/-/async-array-reduce-0.2.1.tgz", @@ -192,6 +224,24 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "optional": true + }, "babel-cli": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.24.1.tgz", @@ -410,7 +460,6 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, "requires": { "babel-runtime": "6.23.0" } @@ -827,7 +876,6 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true, "requires": { "core-js": "2.4.1", "regenerator-runtime": "0.10.5" @@ -867,7 +915,6 @@ "version": "6.25.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", - "dev": true, "requires": { "babel-runtime": "6.23.0", "esutils": "2.0.2", @@ -899,6 +946,15 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -911,6 +967,20 @@ "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=", "dev": true }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "optional": true, + "requires": { + "hoek": "4.2.1" + } + }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", @@ -994,6 +1064,12 @@ } } }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true + }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", @@ -1016,7 +1092,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "2.2.1", "escape-string-regexp": "1.0.5", @@ -1031,6 +1106,29 @@ "integrity": "sha1-A1ALBK2U53jdKJGwnsc6ath7GZY=", "dev": true }, + "cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "requires": { + "css-select": "1.2.0", + "dom-serializer": "0.1.0", + "entities": "1.1.1", + "htmlparser2": "3.9.2", + "lodash.assignin": "4.2.0", + "lodash.bind": "4.2.1", + "lodash.defaults": "4.2.0", + "lodash.filter": "4.6.0", + "lodash.flatten": "4.4.0", + "lodash.foreach": "4.5.0", + "lodash.map": "4.6.0", + "lodash.merge": "4.6.1", + "lodash.pick": "4.4.0", + "lodash.reduce": "4.6.0", + "lodash.reject": "4.6.0", + "lodash.some": "4.6.0" + } + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -1088,8 +1186,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-point-at": { "version": "1.1.0", @@ -1097,6 +1194,19 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, + "color-logger": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/color-logger/-/color-logger-0.0.3.tgz", + "integrity": "sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, "commander": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.10.0.tgz", @@ -1251,14 +1361,62 @@ "core-js": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", - "dev": true + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=" }, "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=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "optional": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "optional": true, + "requires": { + "hoek": "4.2.1" + } + } + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "1.0.0", + "css-what": "2.1.0", + "domutils": "1.5.1", + "nth-check": "1.0.1" + } + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=" + }, + "cssom": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", + "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=" + }, + "cssstyle": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", + "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "optional": true, + "requires": { + "cssom": "0.3.2" + } }, "currently-unhandled": { "version": "0.4.1", @@ -1289,6 +1447,15 @@ "es5-ext": "0.10.23" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "requires": { + "assert-plus": "1.0.0" + } + }, "date-fns": { "version": "1.28.5", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz", @@ -1324,8 +1491,7 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "define-properties": { "version": "1.1.2", @@ -1366,6 +1532,11 @@ "rimraf": "2.6.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "depd": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", @@ -1382,7 +1553,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, "requires": { "repeating": "2.0.1" } @@ -1397,12 +1567,59 @@ "isarray": "1.0.0" } }, + "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=" + } + } + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" + }, + "domhandler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", + "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", + "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" + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1415,6 +1632,11 @@ "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", "dev": true }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", @@ -1529,14 +1751,33 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "optional": true, + "requires": { + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } }, "escope": { "version": "3.6.0", @@ -1550,6 +1791,233 @@ "estraverse": "4.2.0" } }, + "esdoc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esdoc/-/esdoc-1.0.4.tgz", + "integrity": "sha512-Hy5sg0Lec4EDHVem3gFqNi+o6ZptivmaiHYacZhmn3hzLnHSMg2C1L0XTsDIcb4Cxd9aUnWdLAu6a6ghH/LLug==", + "requires": { + "babel-generator": "6.26.0", + "babel-traverse": "6.26.0", + "babylon": "6.18.0", + "cheerio": "0.22.0", + "color-logger": "0.0.3", + "escape-html": "1.0.3", + "fs-extra": "1.0.0", + "ice-cap": "0.0.4", + "marked": "0.3.6", + "minimist": "1.2.0", + "taffydb": "2.7.2" + }, + "dependencies": { + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-generator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.6", + "trim-right": "1.0.1" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "2.4.1", + "regenerator-runtime": "0.11.1" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.8", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "esdoc-accessor-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-accessor-plugin/-/esdoc-accessor-plugin-1.0.0.tgz", + "integrity": "sha1-eRukhy5sQDUVznSbE0jW8Ck62es=" + }, + "esdoc-brand-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-brand-plugin/-/esdoc-brand-plugin-1.0.0.tgz", + "integrity": "sha1-niFtc15i/OxJ96M5u0Eh2mfMYDM=", + "requires": { + "cheerio": "0.22.0" + } + }, + "esdoc-coverage-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esdoc-coverage-plugin/-/esdoc-coverage-plugin-1.1.0.tgz", + "integrity": "sha1-OGmGnNf4eJH5cmJXh2laKZrs5Fw=" + }, + "esdoc-external-ecmascript-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-external-ecmascript-plugin/-/esdoc-external-ecmascript-plugin-1.0.0.tgz", + "integrity": "sha1-ePVl1KDFGFrGMVJhTc4f4ahmiNs=", + "requires": { + "fs-extra": "1.0.0" + } + }, + "esdoc-integrate-manual-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-integrate-manual-plugin/-/esdoc-integrate-manual-plugin-1.0.0.tgz", + "integrity": "sha1-GFSmqhwIEDXXyMUeO91PtlqkcRw=" + }, + "esdoc-integrate-test-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-integrate-test-plugin/-/esdoc-integrate-test-plugin-1.0.0.tgz", + "integrity": "sha1-4tDQAJD38MNeXS8sAzMnp55T5Ak=" + }, + "esdoc-lint-plugin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esdoc-lint-plugin/-/esdoc-lint-plugin-1.0.1.tgz", + "integrity": "sha1-h77mQD5nbAh/Yb6SxFLWDyxqcOU=" + }, + "esdoc-publish-html-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esdoc-publish-html-plugin/-/esdoc-publish-html-plugin-1.1.0.tgz", + "integrity": "sha1-CT+DN6yhaQIlcss4f/zD9HCwJRM=", + "requires": { + "babel-generator": "6.11.4", + "cheerio": "0.22.0", + "escape-html": "1.0.3", + "fs-extra": "1.0.0", + "ice-cap": "0.0.4", + "marked": "0.3.6", + "taffydb": "2.7.2" + }, + "dependencies": { + "babel-generator": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.11.4.tgz", + "integrity": "sha1-FPaTOrsgxiZm0n47e59bncBxKpo=", + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.23.0", + "babel-types": "6.25.0", + "detect-indent": "3.0.1", + "lodash": "4.17.4", + "source-map": "0.5.6" + } + }, + "detect-indent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", + "integrity": "sha1-ncXl3bzu+DJXZLlFGwK8bVQIT3U=", + "requires": { + "get-stdin": "4.0.1", + "minimist": "1.2.0", + "repeating": "1.1.3" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "repeating": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=", + "requires": { + "is-finite": "1.0.2" + } + } + } + }, + "esdoc-standard-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-standard-plugin/-/esdoc-standard-plugin-1.0.0.tgz", + "integrity": "sha1-ZhIBysfvhokkkCRG/awVJyU8XU0=", + "requires": { + "esdoc-accessor-plugin": "1.0.0", + "esdoc-brand-plugin": "1.0.0", + "esdoc-coverage-plugin": "1.1.0", + "esdoc-external-ecmascript-plugin": "1.0.0", + "esdoc-integrate-manual-plugin": "1.0.0", + "esdoc-integrate-test-plugin": "1.0.0", + "esdoc-lint-plugin": "1.0.1", + "esdoc-publish-html-plugin": "1.1.0", + "esdoc-type-inference-plugin": "1.0.1", + "esdoc-undocumented-identifier-plugin": "1.0.0", + "esdoc-unexported-identifier-plugin": "1.0.0" + } + }, + "esdoc-type-inference-plugin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esdoc-type-inference-plugin/-/esdoc-type-inference-plugin-1.0.1.tgz", + "integrity": "sha1-qrynhkH5m9Hs5vMC8EW71jG+cvU=" + }, + "esdoc-undocumented-identifier-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-undocumented-identifier-plugin/-/esdoc-undocumented-identifier-plugin-1.0.0.tgz", + "integrity": "sha1-guBdNxwy0ShxFA8dXIHsmf2cwsg=" + }, + "esdoc-unexported-identifier-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-unexported-identifier-plugin/-/esdoc-unexported-identifier-plugin-1.0.0.tgz", + "integrity": "sha1-H5h0xqfCvr+a05fDzrdcnGnaurE=" + }, "eslint": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", @@ -1738,8 +2206,7 @@ "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" }, "esquery": { "version": "1.0.0", @@ -1763,8 +2230,7 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" }, "estree-walker": { "version": "0.2.1", @@ -1775,8 +2241,7 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "event-emitter": { "version": "0.3.5", @@ -1830,8 +2295,7 @@ "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "extend-shallow": { "version": "2.0.1", @@ -1851,23 +2315,39 @@ "is-extglob": "1.0.0" } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, "ez-async": { "version": "1.0.0-alpha.1", "resolved": "https://registry.npmjs.org/ez-async/-/ez-async-1.0.0-alpha.1.tgz", "integrity": "sha1-ysNCuPqJAm7+c6Jg/p9rgE9J5H8=", "dev": true }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "optional": true + }, "fast-diff": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", "dev": true }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "optional": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "faye-websocket": { "version": "0.11.1", @@ -1996,6 +2476,23 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", "dev": true }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.15" + } + }, "from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -2008,6 +2505,16 @@ "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", "dev": true }, + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0", + "klaw": "1.3.1" + } + }, "fs-readdir-recursive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz", @@ -2047,6 +2554,15 @@ "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "requires": { + "assert-plus": "1.0.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2105,8 +2621,7 @@ "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" }, "globby": { "version": "5.0.0", @@ -2125,8 +2640,7 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "graceful-readlink": { "version": "1.0.1", @@ -2134,6 +2648,36 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "optional": true, + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "optional": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + } + } + }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", @@ -2147,7 +2691,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -2167,6 +2710,23 @@ "is-glob": "2.0.1" } }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "optional": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -2192,6 +2752,19 @@ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.4.1", + "domutils": "1.5.1", + "entities": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, "http-auth": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-3.1.3.tgz", @@ -2216,6 +2789,89 @@ "statuses": "1.3.1" } }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "ice-cap": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ice-cap/-/ice-cap-0.0.4.tgz", + "integrity": "sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg=", + "requires": { + "cheerio": "0.20.0", + "color-logger": "0.0.3" + }, + "dependencies": { + "cheerio": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", + "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", + "requires": { + "css-select": "1.2.0", + "dom-serializer": "0.1.0", + "entities": "1.1.1", + "htmlparser2": "3.8.3", + "jsdom": "7.2.2", + "lodash": "4.17.4" + } + }, + "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" + } + }, + "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": { + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" + } + } + }, + "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=" + } + } + }, "ignore": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", @@ -2250,8 +2906,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.4", @@ -2290,7 +2945,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true, "requires": { "loose-envify": "1.3.1" } @@ -2368,7 +3022,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, "requires": { "number-is-nan": "1.0.1" } @@ -2484,6 +3137,12 @@ "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", "dev": true }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -2511,8 +3170,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -2529,11 +3187,16 @@ "isarray": "1.0.0" } }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.8.4", @@ -2545,11 +3208,59 @@ "esprima": "3.1.3" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "jsdom": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz", + "integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=", + "optional": true, + "requires": { + "abab": "1.0.4", + "acorn": "2.7.0", + "acorn-globals": "1.0.9", + "cssom": "0.3.2", + "cssstyle": "0.2.37", + "escodegen": "1.9.1", + "nwmatcher": "1.4.3", + "parse5": "1.5.1", + "request": "2.83.0", + "sax": "1.2.4", + "symbol-tree": "3.2.2", + "tough-cookie": "2.3.4", + "webidl-conversions": "2.0.1", + "whatwg-url-compat": "0.6.5", + "xml-name-validator": "2.0.1" + }, + "dependencies": { + "acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", + "optional": true + } + } + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "optional": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "optional": true }, "json-stable-stringify": { "version": "1.0.1", @@ -2560,12 +3271,26 @@ "jsonify": "0.0.0" } }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + } + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -2578,6 +3303,18 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "jsx-ast-utils": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", @@ -2593,6 +3330,14 @@ "is-buffer": "1.1.5" } }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "4.1.11" + } + }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", @@ -2606,7 +3351,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, "requires": { "prelude-ls": "1.1.2", "type-check": "0.3.2" @@ -2786,8 +3530,17 @@ "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" }, "lodash.cond": { "version": "4.5.2", @@ -2795,6 +3548,56 @@ "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", "dev": true }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -2805,7 +3608,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, "requires": { "js-tokens": "3.0.2" } @@ -2841,6 +3643,11 @@ "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, + "marked": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=" + }, "matched": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/matched/-/matched-0.4.4.tgz", @@ -2908,14 +3715,12 @@ "mime-db": { "version": "1.27.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" }, "mime-types": { "version": "2.1.15", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true, "requires": { "mime-db": "1.27.0" } @@ -3001,11 +3806,30 @@ "remove-trailing-separator": "1.0.2" } }, + "nth-check": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "requires": { + "boolbase": "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=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwmatcher": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", + "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==", + "optional": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3074,7 +3898,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, "requires": { "deep-is": "0.1.3", "fast-levenshtein": "2.0.6", @@ -3087,8 +3910,7 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" } } }, @@ -3163,6 +3985,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", + "optional": true + }, "parseurl": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", @@ -3216,6 +4044,12 @@ "through": "2.3.8" } }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -3296,8 +4130,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "preserve": { "version": "0.2.0", @@ -3314,8 +4147,7 @@ "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=", - "dev": true + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { "version": "1.1.8", @@ -3323,6 +4155,17 @@ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "optional": true + }, "quill": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.5.tgz", @@ -3460,7 +4303,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -3522,8 +4364,7 @@ "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" }, "regenerator-transform": { "version": "0.9.11", @@ -3602,11 +4443,57 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, "requires": { "is-finite": "1.0.2" } }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "optional": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + }, + "dependencies": { + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "optional": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "optional": true, + "requires": { + "mime-db": "1.33.0" + } + } + } + }, "require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", @@ -3830,8 +4717,13 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "optional": true }, "semver": { "version": "5.3.0", @@ -3904,11 +4796,19 @@ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "optional": true, + "requires": { + "hoek": "4.2.1" + } + }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" }, "source-map-support": { "version": "0.4.15", @@ -3961,6 +4861,22 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, "stack-generator": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.1.tgz", @@ -4049,15 +4965,6 @@ "duplexer": "0.1.1" } }, - "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==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -4069,11 +4976,24 @@ "strip-ansi": "3.0.1" } }, + "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" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "optional": true + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -4110,8 +5030,13 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "optional": true }, "table": { "version": "3.8.3", @@ -4160,6 +5085,11 @@ } } }, + "taffydb": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.2.tgz", + "integrity": "sha1-e/gQalwaSCUbPjvAoOFzJIn9Dcg=" + }, "tag-dist-files": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/tag-dist-files/-/tag-dist-files-0.1.6.tgz", @@ -4193,8 +5123,7 @@ "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, "to-object-path": { "version": "0.3.0", @@ -4205,6 +5134,20 @@ "kind-of": "3.2.2" } }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "1.4.1" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true + }, "tree-kill": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.1.0.tgz", @@ -4220,8 +5163,7 @@ "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, "tryit": { "version": "1.0.3", @@ -4229,11 +5171,25 @@ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, "requires": { "prelude-ls": "1.1.2" } @@ -4289,8 +5245,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.0", @@ -4301,8 +5256,7 @@ "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "v8flags": { "version": "2.1.1", @@ -4329,12 +5283,29 @@ "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", "dev": true }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, "vlq": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=", "dev": true }, + "webidl-conversions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", + "integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=", + "optional": true + }, "websocket-driver": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", @@ -4350,6 +5321,15 @@ "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", "dev": true }, + "whatwg-url-compat": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz", + "integrity": "sha1-AImBEa9om7CXVBzVpFymyHmERb8=", + "optional": true, + "requires": { + "tr46": "0.0.3" + } + }, "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", @@ -4386,6 +5366,12 @@ "mkdirp": "0.5.1" } }, + "xml-name-validator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", + "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", + "optional": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index b53df6ef..a936fb9d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "scripts": { "test": "npm run lint", "debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'", - "lint": "standard", + "lint": "standard && documentation lint src/**", + "docs": "documentation build src/** -f html -o docs", + "serve-docs": "documentation serve src/**", "dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js", "watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'", "postversion": "npm run dist", @@ -64,7 +66,9 @@ "rollup-regenerator-runtime": "^6.23.1", "rollup-watch": "^3.2.2", "standard": "^10.0.2", - "tag-dist-files": "^0.1.6" + "tag-dist-files": "^0.1.6", + "esdoc": "^1.0.4", + "esdoc-standard-plugin": "^1.0.0" }, "dependencies": { "debug": "^2.6.8" diff --git a/src/Binary/Decoder.js b/src/Binary/Decoder.js index fcc18b72..d872e037 100644 --- a/src/Binary/Decoder.js +++ b/src/Binary/Decoder.js @@ -1,7 +1,13 @@ import ID from '../Util/ID.js' import { default as RootID, RootFakeUserID } from '../Util/RootID.js' +/** + * A BinaryDecoder handles the decoding of an ArrayBuffer + */ export default class BinaryDecoder { + /** + * @param {Uint8Array|Buffer} buffer The binary data that this instance decodes + */ constructor (buffer) { if (buffer instanceof ArrayBuffer) { this.uint8arr = new Uint8Array(buffer) @@ -12,6 +18,7 @@ export default class BinaryDecoder { } this.pos = 0 } + /** * Clone this decoder instance * Optionally set a new position parameter @@ -21,26 +28,32 @@ export default class BinaryDecoder { decoder.pos = newPos return decoder } + /** * Number of bytes */ get length () { return this.uint8arr.length } + /** * Skip one byte, jump to the next position */ skip8 () { this.pos++ } + /** * Read one byte as unsigned integer */ readUint8 () { return this.uint8arr[this.pos++] } + /** * Read 4 bytes as unsigned integer + * + * @return number An unsigned integer */ readUint32 () { let uint = @@ -51,19 +64,24 @@ export default class BinaryDecoder { this.pos += 4 return uint } + /** * Look ahead without incrementing position * to the next byte and read it as unsigned integer + * + * @return number An unsigned integer */ peekUint8 () { return this.uint8arr[this.pos] } + /** * Read unsigned integer (32bit) with variable length * 1/8th of the storage is used as encoding overhead - * - numbers < 2^7 is stored in one byte - * - numbers < 2^14 is stored in two bytes - * .. + * * numbers < 2^7 is stored in one byte + * * numbers < 2^14 is stored in two bytes + * + * @return number An unsigned integer */ readVarUint () { let num = 0 @@ -80,9 +98,12 @@ export default class BinaryDecoder { } } } + /** * Read string of variable length - * - varUint is used to store the length of the string + * * varUint is used to store the length of the string + * + * @return string */ readVarString () { let len = this.readVarUint() @@ -94,7 +115,7 @@ export default class BinaryDecoder { return decodeURIComponent(escape(encodedString)) } /** - * Look ahead and read varString without incrementing position + * Look ahead and read varString without incrementing position */ peekVarString () { let pos = this.pos @@ -104,8 +125,10 @@ export default class BinaryDecoder { } /** * Read ID - * - If first varUint read is 0xFFFFFF a RootID is returned - * - Otherwise an ID is returned + * * If first varUint read is 0xFFFFFF a RootID is returned + * * Otherwise an ID is returned + * + * @return ID */ readID () { let user = this.readVarUint() diff --git a/src/Binary/Encoder.js b/src/Binary/Encoder.js index db53e8df..b31cb67d 100644 --- a/src/Binary/Encoder.js +++ b/src/Binary/Encoder.js @@ -3,41 +3,81 @@ import { RootFakeUserID } from '../Util/RootID.js' const bits7 = 0b1111111 const bits8 = 0b11111111 +/** + * A BinaryEncoder handles the encoding to an ArrayBuffer + */ export default class BinaryEncoder { constructor () { // TODO: implement chained Uint8Array buffers instead of Array buffer this.data = [] } + /** + * The current length of the encoded data + */ get length () { return this.data.length } + /** + * The current write pointer (the same as {@link length}). + */ get pos () { return this.data.length } + /** + * Create an ArrayBuffer + * + * @return {Uint8Array} + */ createBuffer () { return Uint8Array.from(this.data).buffer } + /** + * Write one byte as an unsigned integer + * + * @param {number} num The number that is to be encoded + */ writeUint8 (num) { this.data.push(num & bits8) } + /** + * Write one byte as an unsigned Integer at a specific location + * + * @param {number} pos The location where the data will be written + * @param {number} num The number that is to + */ setUint8 (pos, num) { this.data[pos] = num & bits8 } + /** + * Write two bytes as an unsigned integer + * + * @param {number} pos The number that is to be encoded + */ writeUint16 (num) { this.data.push(num & bits8, (num >>> 8) & bits8) } - + /** + * Write two bytes as an unsigned integer at a specific location + * + * @param {number} pos The location where the data will be written + * @param {number} num The number that is to + */ setUint16 (pos, num) { this.data[pos] = num & bits8 this.data[pos + 1] = (num >>> 8) & bits8 } + /** + * Write two bytes as an unsigned integer + * + * @param {number} pos The number that is to be encoded + */ writeUint32 (num) { for (let i = 0; i < 4; i++) { this.data.push(num & bits8) @@ -45,6 +85,12 @@ export default class BinaryEncoder { } } + /** + * Write two bytes as an unsigned integer at a specific location + * + * @param {number} pos The location where the data will be written + * @param {number} num The number that is to + */ setUint32 (pos, num) { for (let i = 0; i < 4; i++) { this.data[pos + i] = num & bits8 @@ -52,6 +98,11 @@ export default class BinaryEncoder { } } + /** + * Write a variable length unsigned integer + * + * @param {number} pos The number that is to be encoded + */ writeVarUint (num) { while (num >= 0b10000000) { this.data.push(0b10000000 | (bits7 & num)) @@ -60,6 +111,11 @@ export default class BinaryEncoder { this.data.push(bits7 & num) } + /** + * Write a variable length string. + * + * @param {number} pos The number that is to be encoded + */ writeVarString (str) { let encodedString = unescape(encodeURIComponent(str)) let bytes = encodedString.split('').map(c => c.codePointAt()) @@ -70,6 +126,11 @@ export default class BinaryEncoder { } } + /** + * Write an ID at the current position + * + * @param {ID} id + */ writeID (id) { const user = id.user this.writeVarUint(user) diff --git a/src/Binding/Binding.js b/src/Binding/Binding.js index a8998410..8ccab2c2 100644 --- a/src/Binding/Binding.js +++ b/src/Binding/Binding.js @@ -1,12 +1,34 @@ import { createMutualExclude } from '../Util/mutualExclude.js' +/** + * Abstract class for bindings + * + * A binding handles data binding from a Yjs type to a data object. For example, + * you can bind a Quill editor instance to a YText instance with the `QuillBinding` class. + * + * It is expected that a concrete implementation accepts two parameters + * (type and binding target). + * + * @example + * const quill = new Quill(document.createElement('div')) + * const type = y.define('quill', Y.Text) + * const binding = new Y.QuillBinding(quill, type) + * + */ export default class Binding { + /** + * @param {YType} type Yjs type + * @param {any} target Binding Target + */ constructor (type, target) { this.type = type this.target = target this._mutualExclude = createMutualExclude() } + /** + * Remove all data observers (both from the type and th target). + */ destroy () { this.type = null this.target = null diff --git a/src/Binding/QuillBinding.js b/src/Binding/QuillBinding.js index 74bb21bc..e88a6c44 100644 --- a/src/Binding/QuillBinding.js +++ b/src/Binding/QuillBinding.js @@ -1,4 +1,3 @@ - import Binding from './Binding.js' function typeObserver (event) { @@ -16,17 +15,29 @@ function quillObserver (delta) { }) } +/** + * A Binding that binds a YText type to a Quill editor + * + * @example + * const quill = new Quill(document.createElement('div')) + * const type = y.define('quill', Y.Text) + * const binding = new Y.QuillBinding(quill, type) + */ export default class QuillBinding extends Binding { - constructor (textType, quillInstance) { - // Binding handles textType as this.type and quillInstance as this.target - super(textType, quillInstance) + /** + * @param {YText} textType + * @param {Quill} quill + */ + constructor (textType, quill) { + // Binding handles textType as this.type and quill as this.target + super(textType, quill) // set initial value - quillInstance.setContents(textType.toDelta(), 'yjs') + quill.setContents(textType.toDelta(), 'yjs') // Observers are handled by this class this._typeObserver = typeObserver.bind(this) this._quillObserver = quillObserver.bind(this) textType.observe(this._typeObserver) - quillInstance.on('text-change', this._quillObserver) + quill.on('text-change', this._quillObserver) } destroy () { // Remove everything that is handled by this class diff --git a/src/Binding/TextareaBinding.js b/src/Binding/TextareaBinding.js index ab59f0f7..829bfe9d 100644 --- a/src/Binding/TextareaBinding.js +++ b/src/Binding/TextareaBinding.js @@ -24,6 +24,17 @@ function domObserver () { }) } +/** + * A binding that binds a YText to a dom textarea. + * + * This binding will automatically be destroyed when it's parent is deleted + * + * @example + * const textare = document.createElement('textarea') + * const type = y.define('textarea', Y.Text) + * const binding = new Y.QuillBinding(textarea, type) + * + */ export default class TextareaBinding extends Binding { constructor (textType, domTextarea) { // Binding handles textType as this.type and domTextarea as this.target diff --git a/src/MessageHandler/binaryEncode.js b/src/MessageHandler/binaryEncode.js index 3a767a13..dafdff36 100644 --- a/src/MessageHandler/binaryEncode.js +++ b/src/MessageHandler/binaryEncode.js @@ -1,8 +1,15 @@ + import { writeStructs } from './syncStep1.js' import { integrateRemoteStructs } from './integrateRemoteStructs.js' import { readDeleteSet, writeDeleteSet } from './deleteSet.js' import BinaryEncoder from '../Binary/Encoder.js' +/** + * Read the Decoder and fill the Yjs instance with data in the decoder. + * + * @param {Y} y The Yjs instance + * @param {BinaryDecoder} decoder The BinaryDecoder to read from. + */ export function fromBinary (y, decoder) { y.transact(function () { integrateRemoteStructs(y, decoder) @@ -10,6 +17,13 @@ export function fromBinary (y, decoder) { }) } +/** + * Encode the Yjs model to binary format. + * + * @param {Y} y The Yjs instance + * @return {BinaryEncoder} The encoder instance that can be transformed + * to ArrayBuffer or Buffer. + */ export function toBinary (y) { let encoder = new BinaryEncoder() writeStructs(y, encoder, new Map()) diff --git a/src/MessageHandler/integrateRemoteStructs.js b/src/MessageHandler/integrateRemoteStructs.js index 97b49211..4e02c752 100644 --- a/src/MessageHandler/integrateRemoteStructs.js +++ b/src/MessageHandler/integrateRemoteStructs.js @@ -11,6 +11,7 @@ class MissingEntry { } /** + * @private * Integrate remote struct * When a remote struct is integrated, other structs might be ready to ready to * integrate. diff --git a/src/MessageHandler/syncStep1.js b/src/MessageHandler/syncStep1.js index 468a7f88..ad73bf6c 100644 --- a/src/MessageHandler/syncStep1.js +++ b/src/MessageHandler/syncStep1.js @@ -30,6 +30,11 @@ export function sendSyncStep1 (connector, syncUser) { connector.send(syncUser, encoder.createBuffer()) } +/** + * @private + * Write all Items that are not not included in ss to + * the encoder object. + */ export function writeStructs (y, encoder, ss) { const lenPos = encoder.pos encoder.writeUint32(0) diff --git a/src/Persistence.js b/src/Persistence.js index 38766bb9..91bbdd2a 100644 --- a/src/Persistence.js +++ b/src/Persistence.js @@ -13,6 +13,10 @@ function getFreshCnf () { } } +/** + * @private + * Abstract persistence class. + */ export default class AbstractPersistence { constructor (opts) { this.opts = opts diff --git a/src/Struct/Delete.js b/src/Struct/Delete.js index a42874b9..c6de21e7 100644 --- a/src/Struct/Delete.js +++ b/src/Struct/Delete.js @@ -3,6 +3,7 @@ import ID from '../Util/ID.js' import { logID } from '../MessageHandler/messageToString.js' /** + * @private * Delete all items in an ID-range * TODO: implement getItemCleanStartNode for better performance (only one lookup) */ @@ -35,13 +36,26 @@ export function deleteItemRange (y, user, clock, range) { } /** - * Delete is not a real struct. It will not be saved in OS + * @private + * A Delete change is not a real Item, but it provides the same interface as an + * Item. The only difference is that it will not be saved in the ItemStore + * (OperationStore), but instead it is safed in the DeleteStore. */ export default class Delete { constructor () { this._target = null this._length = null } + + /** + * @private + * Read the next Item in a Decoder and fill this Item with the read data. + * + * This is called when data is received from a remote peer. + * + * @param {Y} y The Yjs instance that this Item belongs to. + * @param {BinaryDecoder} decoder The decoder object to read data from. + */ _fromBinary (y, decoder) { // TODO: set target, and add it to missing if not found // There is an edge case in p2p networks! @@ -54,15 +68,32 @@ export default class Delete { return [] } } + + /** + * @private + * Transform the properties of this type to binary and write it to an + * BinaryEncoder. + * + * This is called when this Item is sent to a remote peer. + * + * @param {BinaryEncoder} encoder The encoder to write data to. + */ _toBinary (encoder) { encoder.writeUint8(getReference(this.constructor)) encoder.writeID(this._targetID) encoder.writeVarUint(this._length) } + /** - * - If created remotely (a remote user deleted something), + * @private + * Integrates this Item into the shared structure. + * + * This method actually applies the change to the Yjs instance. In the case of + * Delete it marks the delete target as deleted. + * + * * If created remotely (a remote user deleted something), * this Delete is applied to all structs in id-range. - * - If created lokally (e.g. when y-array deletes a range of elements), + * * If created lokally (e.g. when y-array deletes a range of elements), * this struct is broadcasted only (it is already executed) */ _integrate (y, locallyCreated = false) { @@ -78,6 +109,12 @@ export default class Delete { y.persistence.saveStruct(y, this) } } + + /** + * @private + * Transform this Delete to a readable format. + * Useful for logging as all Items implement this method. + */ _logString () { return `Delete - target: ${logID(this._targetID)}, len: ${this._length}` } diff --git a/src/Struct/Item.js b/src/Struct/Item.js index 120c4e75..eba53d8a 100644 --- a/src/Struct/Item.js +++ b/src/Struct/Item.js @@ -5,11 +5,12 @@ import Delete from './Delete.js' import { transactionTypeChanged } from '../Transaction.js' /** - * Helper utility to split an Item (see _splitAt) - * - copy all properties from a to b - * - connect a to b + * @private + * Helper utility to split an Item (see {@link Item#_splitAt}) + * - copies all properties from a to b + * - connects a to b * - assigns the correct _id - * - save b to os + * - saves b to os */ export function splitHelper (y, a, b, diff) { const aID = a._id @@ -46,6 +47,10 @@ export function splitHelper (y, a, b, diff) { } } +/** + * @private + * Abstract class that represents any content. + */ export default class Item { constructor () { this._id = null @@ -58,14 +63,18 @@ export default class Item { this._deleted = false this._redone = null } + /** - * Create a operation with the same effect (without position effect) + * @private + * Creates an Item with the same effect as this Item (without position effect) */ _copy () { return new this.constructor() } + /** - * Redo the effect of this operation. + * @private + * Redoes the effect of this operation. */ _redo (y) { if (this._redone !== null) { @@ -106,27 +115,43 @@ export default class Item { return struct } + /** + * @private + * Computes the last content address of this Item. + */ get _lastId () { return new ID(this._id.user, this._id.clock + this._length - 1) } + + /** + * @private + * Computes the length of this Item. + */ get _length () { return 1 } + /** - * Some elements are not supposed to be addressable. For example, an - * ItemFormat should not be retrievable via yarray.get(pos) + * @private + * Should return false if this Item is some kind of meta information + * (e.g. format information). + * + * * Whether this Item should be addressable via `yarray.get(i)` + * * Whether this Item should be counted when computing yarray.length */ get _countable () { return true } + /** - * Splits this struct so that another struct can be inserted in-between. + * @private + * Splits this Item so that another Items can be inserted in-between. * This must be overwritten if _length > 1 * Returns right part after split - * - diff === 0 => this - * - diff === length => this._right - * - otherwise => split _content and return right part of split - * (see ItemJSON/ItemString for implementation) + * * diff === 0 => this + * * diff === length => this._right + * * otherwise => split _content and return right part of split + * (see {@link ItemJSON}/{@link ItemString} for implementation) */ _splitAt (y, diff) { if (diff === 0) { @@ -134,6 +159,15 @@ export default class Item { } return this._right } + + /** + * @private + * Mark this Item as deleted. + * + * @param {Y} y The Yjs instance + * @param {boolean} createDelete Whether to propagate a message that this + * Type was deleted. + */ _delete (y, createDelete = true) { if (!this._deleted) { this._deleted = true @@ -152,17 +186,27 @@ export default class Item { y._transaction.deletedStructs.add(this) } } + /** - * This is called right before this struct receives any children. + * @private + * This is called right before this Item receives any children. * It can be overwritten to apply pending changes before applying remote changes */ _beforeChange () { // nop } - /* - * - Integrate the struct so that other types/structs can see it - * - Add this struct to y.os - * - Check if this is struct deleted + + /** + * @private + * Integrates this Item into the shared structure. + * + * This method actually applies the change to the Yjs instance. In case of + * Item it connects _left and _right to this Item and calls the + * {@link Item#beforeChange} method. + * + * * Integrate the struct so that other types/structs can see it + * * Add this struct to y.os + * * Check if this is struct deleted */ _integrate (y) { y._transaction.newTypes.add(this) @@ -188,6 +232,7 @@ export default class Item { // or this types is new this._parent._beforeChange() } + /* # $this has to find a unique position between origin and the next known character # case 1: $origin equals $o.origin: the $creator parameter decides if left or right @@ -280,6 +325,16 @@ export default class Item { } } } + + /** + * @private + * Transform the properties of this type to binary and write it to an + * BinaryEncoder. + * + * This is called when this Item is sent to a remote peer. + * + * @param {BinaryEncoder} encoder The encoder to write data to. + */ _toBinary (encoder) { encoder.writeUint8(getReference(this.constructor)) let info = 0 @@ -320,6 +375,16 @@ export default class Item { encoder.writeVarString(JSON.stringify(this._parentSub)) } } + + /** + * @private + * Read the next Item in a Decoder and fill this Item with the read data. + * + * This is called when data is received from a remote peer. + * + * @param {Y} y The Yjs instance that this Item belongs to. + * @param {BinaryDecoder} decoder The decoder object to read data from. + */ _fromBinary (y, decoder) { let missing = [] const info = decoder.readUint8() diff --git a/src/Struct/Type.js b/src/Struct/Type.js index 80d93fed..a37aff51 100644 --- a/src/Struct/Type.js +++ b/src/Struct/Type.js @@ -30,6 +30,9 @@ export function getListItemIDByPosition (type, i) { } } +/** + * Abstract Yjs Type class + */ export default class Type extends Item { constructor () { super() @@ -39,6 +42,20 @@ export default class Type extends Item { this._eventHandler = new EventHandler() this._deepEventHandler = new EventHandler() } + + /** + * Compute the path from this type to the specified target. + * + * @example + * It should be accessible via `this.get(result[0]).get(result[1])..`` + * const path = type.getPathTo(child) + * // assuming `type instanceof YArray` + * console.log(path) // might look like => [2, 'key1'] + * child === type.get(path[0]).get(path[1]) + * + * @param {YType} type Type target + * @return {Array} Path to the target + */ getPathTo (type) { if (type === this) { return [] @@ -65,6 +82,12 @@ export default class Type extends Item { } return path } + + /** + * @private + * Call event listeners with an event. This will also add an event to all + * parents (for `.observeDeep` handlers). + */ _callEventHandler (transaction, event) { const changedParentTypes = transaction.changedParentTypes this._eventHandler.callEventListeners(transaction, event) @@ -79,6 +102,14 @@ export default class Type extends Item { type = type._parent } } + + /** + * @private + * Helper method to transact if the y instance is available. + * + * TODO: Currently event handlers are not thrown when a type is not registered + * with a Yjs instance. + */ _transact (f) { const y = this._y if (y !== null) { @@ -87,18 +118,53 @@ export default class Type extends Item { f(y) } } + + /** + * Observe all events that are created on this type. + * + * @param {Function} f Observer function + */ observe (f) { this._eventHandler.addEventListener(f) } + + /** + * Observe all events that are created by this type and its children. + * + * @param {Function} f Observer function + */ observeDeep (f) { this._deepEventHandler.addEventListener(f) } + + /** + * Unregister an observer function. + * + * @param {Function} f Observer function + */ unobserve (f) { this._eventHandler.removeEventListener(f) } + + /** + * Unregister an observer function. + * + * @param {Function} f Observer function + */ unobserveDeep (f) { this._deepEventHandler.removeEventListener(f) } + + /** + * @private + * Integrate this type into the Yjs instance. + * + * * Save this struct in the os + * * This type is sent to other client + * * Observer functions are fired + * + * @param {Y} y The Yjs instance + */ _integrate (y) { super._integrate(y) this._y = y @@ -117,6 +183,15 @@ export default class Type extends Item { integrateChildren(y, t) } } + + /** + * @private + * Mark this Item as deleted. + * + * @param {Y} y The Yjs instance + * @param {boolean} createDelete Whether to propagate a message that this + * Type was deleted. + */ _delete (y, createDelete) { super._delete(y, createDelete) y._transaction.changedTypes.delete(this) diff --git a/src/Transaction.js b/src/Transaction.js index 6a99a96b..4b372fdf 100644 --- a/src/Transaction.js +++ b/src/Transaction.js @@ -1,4 +1,12 @@ +/** + * Changes that are created within a transaction are bundled and sent as one + * message to the remote peers. This implies that the changes are applied + * in one flush and at most one {@link YEvent} per type is created. + * + * It is best to bundle as many changes in a single Transaction as possible. + * This way only few changes need to be computed + */ export default class Transaction { constructor (y) { this.y = y diff --git a/src/Type/YArray.js b/src/Type/YArray.js index b017266b..5ef5e50a 100644 --- a/src/Type/YArray.js +++ b/src/Type/YArray.js @@ -4,6 +4,13 @@ import ItemString from '../Struct/ItemString.js' import { logID } from '../MessageHandler/messageToString.js' import YEvent from '../Util/YEvent.js' +/** + * Event that describes the changes on a YArray + * + * @param {YArray} yarray The changed type + * @param {Boolean} remote Whether the changed was caused by a remote peer + * @param {Transaction} transaction The transaction object + */ export class YArrayEvent extends YEvent { constructor (yarray, remote, transaction) { super(yarray) @@ -12,6 +19,12 @@ export class YArrayEvent extends YEvent { this._addedElements = null this._removedElements = null } + + /** + * Child elements that were added in this transaction. + * + * @return {Set} + */ get addedElements () { if (this._addedElements === null) { const target = this.target @@ -26,6 +39,12 @@ export class YArrayEvent extends YEvent { } return this._addedElements } + + /** + * Child elements that were removed in this transaction. + * + * @return {Set} + */ get removedElements () { if (this._removedElements === null) { const target = this.target @@ -42,29 +61,54 @@ export class YArrayEvent extends YEvent { } } +/** + * A shared Array implementation. + */ export default class YArray extends Type { + /** + * @private + * Creates YArray Event and calls observers. + */ _callObserver (transaction, parentSubs, remote) { this._callEventHandler(transaction, new YArrayEvent(this, remote, transaction)) } - get (pos) { + + /** + * Returns the i-th element from a YArray. + * + * @param {Integer} index The index of the element to return from the YArray + */ + get (index) { let n = this._start while (n !== null) { if (!n._deleted && n._countable) { - if (pos < n._length) { + if (index < n._length) { if (n.constructor === ItemJSON || n.constructor === ItemString) { - return n._content[pos] + return n._content[index] } else { return n } } - pos -= n._length + index -= n._length } n = n._right } } + + /** + * Transforms this YArray to a JavaScript Array. + * + * @return {Array} + */ toArray () { return this.map(c => c) } + + /** + * Transforms this Shared Type to a JSON object. + * + * @return {Array} + */ toJSON () { return this.map(c => { if (c instanceof Type) { @@ -77,6 +121,15 @@ export default class YArray extends Type { return c }) } + + /** + * Returns an Array with the result of calling a provided function on every + * element of this YArray. + * + * @param {Function} f Function that produces an element of the new Array + * @return {Array} A new array with each element being the result of the + * callback function + */ map (f) { const res = [] this.forEach((c, i) => { @@ -84,25 +137,35 @@ export default class YArray extends Type { }) return res } + + /** + * Executes a provided function on once on overy element of this YArray. + * + * @param {Function} f A function to execute on every element of this YArray. + */ forEach (f) { - let pos = 0 + let index = 0 let n = this._start while (n !== null) { if (!n._deleted && n._countable) { if (n instanceof Type) { - f(n, pos++, this) + f(n, index++, this) } else { const content = n._content const contentLen = content.length for (let i = 0; i < contentLen; i++) { - pos++ - f(content[i], pos, this) + index++ + f(content[i], index, this) } } } n = n._right } } + + /** + * Computes the length of this YArray. + */ get length () { let length = 0 let n = this._start @@ -114,6 +177,7 @@ export default class YArray extends Type { } return length } + [Symbol.iterator] () { return { next: function () { @@ -143,14 +207,21 @@ export default class YArray extends Type { _count: 0 } } - delete (pos, length = 1) { + + /** + * Deletes elements starting from an index. + * + * @param {Integer} index Index at which to start deleting elements + * @param {Integer} length The number of elements to remove. Defaults to 1. + */ + delete (index, length = 1) { this._y.transact(() => { let item = this._start let count = 0 while (item !== null && length > 0) { if (!item._deleted && item._countable) { - if (count <= pos && pos < count + item._length) { - const diffDel = pos - count + if (count <= index && index < count + item._length) { + const diffDel = index - count item = item._splitAt(this._y, diffDel) item._splitAt(this._y, length) length -= item._length @@ -167,6 +238,14 @@ export default class YArray extends Type { throw new Error('Delete exceeds the range of the YArray') } } + + /** + * @private + * Inserts content after an element container. + * + * @param {Item} left The element container to use as a reference. + * @param {Array} content The Array of content to insert (see {@see insert}) + */ insertAfter (left, content) { this._transact(y => { let right @@ -224,7 +303,24 @@ export default class YArray extends Type { } }) } - insert (pos, content) { + + /** + * Inserts new content at an index. + * + * Important: This function expects an array of content. Not just a content + * object. The reason for this "weirdness" is that inserting several elements + * is very efficient when it is done as a single operation. + * + * @example + * // Insert character 'a' at position 0 + * yarray.insert(0, ['a']) + * // Insert numbers 1, 2 at position 1 + * yarray.insert(2, [1, 2]) + * + * @param {Integer} index The index to insert content at. + * @param {Array} content The array of content + */ + insert (index, content) { this._transact(() => { let left = null let right = this._start @@ -232,8 +328,8 @@ export default class YArray extends Type { const y = this._y while (right !== null) { const rightLen = right._deleted ? 0 : (right._length - 1) - if (count <= pos && pos <= count + rightLen) { - const splitDiff = pos - count + if (count <= index && index <= count + rightLen) { + const splitDiff = index - count right = right._splitAt(y, splitDiff) left = right._left count += splitDiff @@ -245,12 +341,18 @@ export default class YArray extends Type { left = right right = right._right } - if (pos > count) { - throw new Error('Position exceeds array range!') + if (index > count) { + throw new Error('Index exceeds array range!') } this.insertAfter(left, content) }) } + + /** + * Appends content to this YArray. + * + * @param {Array} content Array of content to append. + */ push (content) { let n = this._start let lastUndeleted = null @@ -262,6 +364,12 @@ export default class YArray extends Type { } this.insertAfter(lastUndeleted, content) } + + /** + * @private + * Transform this YArray to a readable format. + * Useful for logging as all Items implement this method. + */ _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null diff --git a/src/Type/YMap.js b/src/Type/YMap.js index 06391571..6103b127 100644 --- a/src/Type/YMap.js +++ b/src/Type/YMap.js @@ -4,7 +4,14 @@ import ItemJSON from '../Struct/ItemJSON.js' import { logID } from '../MessageHandler/messageToString.js' import YEvent from '../Util/YEvent.js' -class YMapEvent extends YEvent { +/** + * Event that describes the changes on a YMap. + * + * @param {YMap} ymap The YArray that changed. + * @param {Set} subs The keys that changed. + * @param {boolean} remote Whether the change was created by a remote peer. + */ +export class YMapEvent extends YEvent { constructor (ymap, subs, remote) { super(ymap) this.keysChanged = subs @@ -12,10 +19,23 @@ class YMapEvent extends YEvent { } } +/** + * A shared Map implementation. + */ export default class YMap extends Type { + /** + * @private + * Creates YMap Event and calls observers. + */ _callObserver (transaction, parentSubs, remote) { this._callEventHandler(transaction, new YMapEvent(this, parentSubs, remote)) } + + /** + * Transforms this Shared Type to a JSON object. + * + * @return {Object} + */ toJSON () { const map = {} for (let [key, item] of this._map) { @@ -35,7 +55,14 @@ export default class YMap extends Type { } return map } + + /** + * Returns the keys for each element in the YMap Type. + * + * @return {Array} + */ keys () { + // TODO: Should return either Iterator or Set! let keys = [] for (let [key, value] of this._map) { if (!value._deleted) { @@ -44,6 +71,12 @@ export default class YMap extends Type { } return keys } + + /** + * Remove a specified element from this YMap. + * + * @param {encodable} key The key of the element to remove. + */ delete (key) { this._transact((y) => { let c = this._map.get(key) @@ -52,11 +85,22 @@ export default class YMap extends Type { } }) } + + /** + * Adds or updates an element with a specified key and value. + * + * @param {encodable} key The key of the element to add to this YMap. + * @param {encodable | YType} value The value of the element to add to this + * YMap. + */ set (key, value) { this._transact(y => { const old = this._map.get(key) || null if (old !== null) { - if (old.constructor === ItemJSON && !old._deleted && old._content[0] === value) { + if ( + old.constructor === ItemJSON && + !old._deleted && old._content[0] === value + ) { // Trying to overwrite with same value // break here return value @@ -87,6 +131,12 @@ export default class YMap extends Type { }) return value } + + /** + * Returns a specified element from this YMap. + * + * @param {encodable} key The key of the element to return. + */ get (key) { let v = this._map.get(key) if (v === undefined || v._deleted) { @@ -98,6 +148,12 @@ export default class YMap extends Type { return v._content[v._content.length - 1] } } + + /** + * Returns a boolean indicating whether the specified key exists or not. + * + * @param {encodable} key The key to test. + */ has (key) { let v = this._map.get(key) if (v === undefined || v._deleted) { @@ -106,6 +162,12 @@ export default class YMap extends Type { return true } } + + /** + * @private + * Transform this YMap to a readable format. + * Useful for logging as all Items implement this method. + */ _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null diff --git a/src/Type/YText.js b/src/Type/YText.js index 926d8ef7..3f565e53 100644 --- a/src/Type/YText.js +++ b/src/Type/YText.js @@ -46,11 +46,11 @@ function findNextPosition (currentAttributes, parent, left, right, count) { return [left, right, currentAttributes] } -function findPosition (parent, pos) { +function findPosition (parent, index) { let currentAttributes = new Map() let left = null let right = parent._start - return findNextPosition(currentAttributes, parent, left, right, pos) + return findNextPosition(currentAttributes, parent, left, right, index) } // negate applied formats @@ -212,11 +212,50 @@ function deleteText (y, length, parent, left, right, currentAttributes) { return [left, right] } +// TODO: In the quill delta representation we should also use the format {ops:[..]} +/** + * The Quill Delta format represents changes on a text document with + * formatting information. For mor information visit {@link https://quilljs.com/docs/delta/|Quill Delta} + * + * @example + * { + * ops: [ + * { insert: 'Gandalf', attributes: { bold: true } }, + * { insert: ' the ' }, + * { insert: 'Grey', attributes: { color: '#cccccc' } } + * ] + * } + * + * @typedef {Array} Delta + */ + + /** + * Attributes that can be assigned to a selection of text. + * + * @example + * { + * bold: true, + * font-size: '40px' + * } + * + * @typedef {Object} TextAttributes + */ + +/** + * Event that describes the changes on a YText type. + */ class YTextEvent extends YArrayEvent { constructor (ytext, remote, transaction) { super(ytext, remote, transaction) this._delta = null } + + /** + * Compute the changes in the delta format. + * + * @return {Delta} A {@link https://quilljs.com/docs/delta/|Quill Delta}) that + * represents the changes on the document. + */ get delta () { if (this._delta === null) { const y = this.target._y @@ -378,6 +417,15 @@ class YTextEvent extends YArrayEvent { } } +/** + * Type that represents text with formatting information. + * + * This type replaces y-richtext as this implementation is able to handle + * block formats (format information on a paragraph), embeds (complex elements + * like pictures and videos), and text formats (**bold**, *italic*). + * + * @param {String} string The initial value of the YText. + */ export default class YText extends YArray { constructor (string) { super() @@ -388,9 +436,18 @@ export default class YText extends YArray { this._start = start } } + + /** + * @private + * Creates YMap Event and calls observers. + */ _callObserver (transaction, parentSubs, remote) { this._callEventHandler(transaction, new YTextEvent(this, remote, transaction)) } + + /** + * Returns the unformatted string representation of this YText type. + */ toString () { let str = '' let n = this._start @@ -402,6 +459,12 @@ export default class YText extends YArray { } return str } + + /** + * Apply a {@link Delta} on this shared YText type. + * + * @param {Delta} delta The changes to apply on this element. + */ applyDelta (delta) { this._transact(y => { let left = null @@ -419,8 +482,11 @@ export default class YText extends YArray { } }) } + /** - * As defined by Quilljs - https://quilljs.com/docs/delta/ + * Returns the Delta representation of this YText type. + * + * @return {Delta} The Delta representation of this type. */ toDelta () { let ops = [] @@ -461,42 +527,84 @@ export default class YText extends YArray { packStr() return ops } - insert (pos, text, attributes = {}) { + + /** + * Insert text at a given index. + * + * @param {Integer} index The index at which to start inserting. + * @param {String} text The text to insert at the specified position. + * @param {TextAttributes} attributes Optionally define some formatting + * information to apply on the inserted + * Text. + */ + insert (index, text, attributes = {}) { if (text.length <= 0) { return } this._transact(y => { - let [left, right, currentAttributes] = findPosition(this, pos) + let [left, right, currentAttributes] = findPosition(this, index) insertText(y, text, this, left, right, currentAttributes, attributes) }) } - insertEmbed (pos, embed, attributes = {}) { + + /** + * Inserts an embed at a index. + * + * @param {Integer} index The index to insert the embed at. + * @param {Object} embed The Object that represents the embed. + * @param {TextAttributes} attributes Attribute information to apply on the + * embed + * + */ + insertEmbed (index, embed, attributes = {}) { if (embed.constructor !== Object) { throw new Error('Embed must be an Object') } this._transact(y => { - let [left, right, currentAttributes] = findPosition(this, pos) + let [left, right, currentAttributes] = findPosition(this, index) insertText(y, embed, this, left, right, currentAttributes, attributes) }) } - delete (pos, length) { + + /** + * Deletes text starting from an index. + * + * @param {Integer} index Index at which to start deleting. + * @param {Integer} length The number of characters to remove. Defaults to 1. + */ + delete (index, length) { if (length === 0) { return } this._transact(y => { - let [left, right, currentAttributes] = findPosition(this, pos) + let [left, right, currentAttributes] = findPosition(this, index) deleteText(y, length, this, left, right, currentAttributes) }) } - format (pos, length, attributes) { + + /** + * Assigns properties to a range of text. + * + * @param {Integer} index The position where to start formatting. + * @param {Integer} length The amount of characters to assign properties to. + * @param {TextAttributes} attributes Attribute information to apply on the + * text. + */ + format (index, length, attributes) { this._transact(y => { - let [left, right, currentAttributes] = findPosition(this, pos) + let [left, right, currentAttributes] = findPosition(this, index) if (right === null) { return } formatText(y, length, this, left, right, currentAttributes, attributes) }) } + + /** + * @private + * Transform this YText to a readable format. + * Useful for logging as all Items implement this method. + */ _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null diff --git a/src/Type/y-xml/YXmlElement.js b/src/Type/y-xml/YXmlElement.js index c795879c..bbfaf7c3 100644 --- a/src/Type/y-xml/YXmlElement.js +++ b/src/Type/y-xml/YXmlElement.js @@ -3,6 +3,16 @@ import { defaultDomFilter } from './utils.js' import YMap from '../YMap.js' import { YXmlFragment } from './y-xml.js' +/** + * An YXmlElement imitates the behavior of a + * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}. + * + * * An YXmlElement has attributes (key value pairs) + * * An YXmlElement has childElements that must inherit from YXmlElement + * + * @param {String} arg1 Node name + * @param {Function} arg2 Dom filter + */ export default class YXmlElement extends YXmlFragment { constructor (arg1, arg2, _document) { super() @@ -20,11 +30,21 @@ export default class YXmlElement extends YXmlFragment { this._domFilter = arg2 } } + + /** + * @private + * Creates an Item with the same effect as this Item (without position effect) + */ _copy () { let struct = super._copy() struct.nodeName = this.nodeName return struct } + + /** + * @private + * Copies children and attributes from a dom node to this YXmlElement. + */ _setDom (dom, _document) { if (this._dom != null) { throw new Error('Only call this method if you know what you are doing ;)') @@ -48,20 +68,61 @@ export default class YXmlElement extends YXmlFragment { return dom } } + + /** + * @private + * Bind a dom to to this YXmlElement. This means that the DOM changes when the + * YXmlElement is modified and that this YXmlElement changes when the DOM is + * modified. + * + * Currently only works in YXmlFragment. + */ _bindToDom (dom, _document) { _document = _document || document this._dom = dom dom._yxml = this } + + /** + * @private + * Read the next Item in a Decoder and fill this Item with the read data. + * + * This is called when data is received from a remote peer. + * + * @param {Y} y The Yjs instance that this Item belongs to. + * @param {BinaryDecoder} decoder The decoder object to read data from. + */ _fromBinary (y, decoder) { const missing = super._fromBinary(y, decoder) this.nodeName = decoder.readVarString() return missing } + + /** + * @private + * Transform the properties of this type to binary and write it to an + * BinaryEncoder. + * + * This is called when this Item is sent to a remote peer. + * + * @param {BinaryEncoder} encoder The encoder to write data to. + */ _toBinary (encoder) { super._toBinary(encoder) encoder.writeVarString(this.nodeName) } + + /** + * @private + * Integrates this Item into the shared structure. + * + * This method actually applies the change to the Yjs instance. In case of + * Item it connects _left and _right to this Item and calls the + * {@link Item#beforeChange} method. + * + * * Checks for nodeName + * * Sets domFilter + */ _integrate (y) { if (this.nodeName === null) { throw new Error('nodeName must be defined!') @@ -71,8 +132,9 @@ export default class YXmlElement extends YXmlFragment { } super._integrate(y) } + /** - * Returns the string representation of the XML document. + * Returns the string representation of this YXmlElement. * The attributes are ordered by attribute-name, so you can easily use this * method to compare YXmlElements */ @@ -93,18 +155,42 @@ export default class YXmlElement extends YXmlFragment { const attrsString = stringBuilder.length > 0 ? ' ' + stringBuilder.join(' ') : '' return `<${nodeName}${attrsString}>${super.toString()}` } + + /** + * Removes an attribute from this YXmlElement. + * + * @param {String} attributeName The attribute name that is to be removed. + */ removeAttribute () { return YMap.prototype.delete.apply(this, arguments) } + /** + * Sets or updates an attribute. + * + * @param {String} attributeName The attribute name that is to be set. + * @param {String} attributeValue The attribute value that is to be set. + */ setAttribute () { return YMap.prototype.set.apply(this, arguments) } + /** + * Returns an attribute value that belongs to the attribute name. + * + * @param {String} attributeName The attribute name that identifies the + * queried value. + * @return {String} The queried attribute value + */ getAttribute () { return YMap.prototype.get.apply(this, arguments) } + /** + * Returns all attribute name/value pairs in a JSON Object. + * + * @return {Object} A JSON Object that describes the attributes. + */ getAttributes () { const obj = {} for (let [key, value] of this._map) { @@ -114,6 +200,12 @@ export default class YXmlElement extends YXmlFragment { } return obj } + + /** + * Creates a Dom Element that mirrors this YXmlElement. + * + * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} + */ getDom (_document) { _document = _document || document let dom = this._dom diff --git a/src/Type/y-xml/YXmlEvent.js b/src/Type/y-xml/YXmlEvent.js index b63f300d..b27eb139 100644 --- a/src/Type/y-xml/YXmlEvent.js +++ b/src/Type/y-xml/YXmlEvent.js @@ -1,5 +1,8 @@ import YEvent from '../../Util/YEvent.js' +/** + * An Event that describes changes on a YXml Element or Yxml Fragment + */ export default class YXmlEvent extends YEvent { constructor (target, subs, remote) { super(target) diff --git a/src/Type/y-xml/YXmlFragment.js b/src/Type/y-xml/YXmlFragment.js index ace6b680..96663f03 100644 --- a/src/Type/y-xml/YXmlFragment.js +++ b/src/Type/y-xml/YXmlFragment.js @@ -36,6 +36,24 @@ function domToYXml (parent, doms, _document) { return types } +/** + * Define the elements to which a set of CSS queries apply. + * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors} + * + * @example + * query = '.classSelector' + * query = 'nodeSelector' + * query = '#idSelector' + * + * @typedef {string} CSS_Selector + */ + +/** + * Represents a subset of the nodes of a YXmlElement / YXmlFragment and a + * position within them. + * + * Can be created with {@link YXmlFragment#createTreeWalker} + */ class YXmlTreeWalker { constructor (root, f) { this._filter = f || (() => true) @@ -46,6 +64,11 @@ class YXmlTreeWalker { [Symbol.iterator] () { return this } + /** + * Get the next node. + * + * @return {YXmlElement} The next node. + */ next () { let n = this._currentNode if (this._firstCall) { @@ -84,6 +107,11 @@ class YXmlTreeWalker { } } +/** + * Represents a list of {@link YXmlElement}. + * A YxmlFragment does not have a nodeName and it does not have attributes. + * Therefore it also must not be added as a childElement. + */ export default class YXmlFragment extends YArray { constructor () { super() @@ -110,18 +138,31 @@ export default class YXmlFragment extends YArray { } } } + + /** + * Create a subtree of childNodes. + * + * @param {Function} filter Function that is called on each child element and + * returns a Boolean indicating whether the child + * is to be included in the subtree. + * @return {TreeWalker} A subtree and a position within it. + */ createTreeWalker (filter) { return new YXmlTreeWalker(this, filter) } + /** - * Retrieve first element that matches *query* - * Similar to DOM's querySelector, but only accepts a subset of its queries + * Returns the first YXmlElement that matches the query. + * Similar to DOM's {@link querySelector}. * * Query support: * - tagname * TODO: * - id * - attribute + * + * @param {CSS_Selector} query The query on the children. + * @return {?YXmlElement} The first element that matches the query or null. */ querySelector (query) { query = query.toUpperCase() @@ -133,16 +174,52 @@ export default class YXmlFragment extends YArray { return next.value } } + + /** + * Returns all YXmlElements that match the query. + * Similar to Dom's {@link querySelectorAll}. + * + * TODO: Does not yet support all queries. Currently only query by tagName. + * + * @param {CSS_Selector} query The query on the children + * @return {Array} The elements that match this query. + */ querySelectorAll (query) { query = query.toUpperCase() return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query)) } + + /** + * Enables the smart scrolling functionality for a Dom Binding. + * This is useful when YXml is bound to a shared editor. When activated, + * the viewport will be changed to accommodate remote changes. + * + * @TODO: Disabled for now. + * + * @param {Element} scrollElement The node that is + */ enableSmartScrolling (scrollElement) { this._scrollElement = scrollElement this.forEach(xml => { xml.enableSmartScrolling(scrollElement) }) } + + /** + * Dom filter function. + * + * @callback domFilter + * @param {string} nodeName The nodeName of the element + * @param {Map} attributes The map of attributes. + * @return {boolean} Whether to include the Dom node in the YXmlElement. + */ + + /** + * Filter out Dom elements. + * + * @param {domFilter} f The filtering function that decides whether to include + * a Dom node. + */ setDomFilter (f) { this._domFilter = f let attributes = new Map() @@ -168,16 +245,41 @@ export default class YXmlFragment extends YArray { }) }) } + + /** + * @private + * Creates YArray Event and calls observers. + */ _callObserver (transaction, parentSubs, remote) { this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote)) } + + /** + * Get the string representation of all the children of this YXmlFragment. + * + * @return {string} The string representation of all children. + */ toString () { return this.map(xml => xml.toString()).join('') } + + /** + * @private + * Unbind from Dom and mark this Item as deleted. + * + * @param {Y} y The Yjs instance + * @param {boolean} createDelete Whether to propagate a message that this + * Type was deleted. + */ _delete (y, createDelete) { this._unbindFromDom() super._delete(y, createDelete) } + + /** + * @private + * Unbind this YXmlFragment from the Dom. + */ _unbindFromDom () { if (this._domObserver != null) { this._domObserver.disconnect() @@ -191,19 +293,53 @@ export default class YXmlFragment extends YArray { this._y.off('beforeTransaction', this._beforeTransactionHandler) } } + + /** + * Insert Dom Elements after one of the children of this YXmlFragment. + * The Dom elements will be bound to a new YXmlElement and inserted at the + * specified position. + * + * @param {YXmlElement|null} prev The reference node. New YxmlElements are + * inserted after this node. Set null to insert at + * the beginning. + * @param {Array} doms The Dom elements to insert. + * @return {Array} The YxmlElements that are inserted. + */ insertDomElementsAfter (prev, doms, _document) { const types = domToYXml(this, doms, _document) this.insertAfter(prev, types) return types } - insertDomElements (pos, doms, _document) { + + /** + * Insert Dom Elements at a specified index. + * The Dom elements will be bound to a new YXmlElement and inserted at the + * specified position. + * + * @param {Integer} index The position to insert elements at. + * @param {Array} doms The Dom elements to insert. + * @return {Array} The YxmlElements that are inserted. + */ + insertDomElements (index, doms, _document) { const types = domToYXml(this, doms, _document) - this.insert(pos, types) + this.insert(index, types) return types } + + /** + * Get the Dom representation of this YXml type.. + */ getDom () { return this._dom } + + /** + * Bind this YXmlFragment and all its children to a Dom Element. + * The content of the Dom Element are replaced with the Dom representation of + * the children of this YXml Type. + * + * @param {Element} dom The Dom Element that should be bound to this Type. + */ bindToDom (dom, _document) { if (this._dom != null) { this._unbindFromDom() @@ -217,8 +353,12 @@ export default class YXmlFragment extends YArray { }) this._bindToDom(dom, _document) } - // binds to a dom element - // Only call if dom and YXml are isomorph + + /** + * @private + * Binds to a dom element. + * Only call if dom and YXml are isomorph + */ _bindToDom (dom, _document) { _document = _document || document this._dom = dom @@ -346,6 +486,12 @@ export default class YXmlFragment extends YArray { } return dom } + + /** + * @private + * Transform this YXml Type to a readable format. + * Useful for logging as all Items implement this method. + */ _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null diff --git a/src/Type/y-xml/YXmlHook.js b/src/Type/y-xml/YXmlHook.js index ee2ce4f7..a5834ab2 100644 --- a/src/Type/y-xml/YXmlHook.js +++ b/src/Type/y-xml/YXmlHook.js @@ -1,6 +1,11 @@ import YMap from '../YMap.js' import { getHook, addHook } from './hooks.js' +/** + * You can manage binding to a custom type with YXmlHook. + * + * @param {String} hookName nodeName of the Dom Node. + */ export default class YXmlHook extends YMap { constructor (hookName, dom) { super() @@ -14,11 +19,20 @@ export default class YXmlHook extends YMap { getHook(hookName).fillType(dom, this) } } + + /** + * @private + * Creates an Item with the same effect as this Item (without position effect) + */ _copy () { const struct = super._copy() struct.hookName = this.hookName return struct } + + /** + * Returns the Dom representation of this YXmlHook. + */ getDom (_document) { _document = _document || document if (this._dom === null) { @@ -29,20 +43,56 @@ export default class YXmlHook extends YMap { } return this._dom } + + /** + * @private + * Removes the Dom binding. + */ _unbindFromDom () { this._dom._yxml = null this._yxml = null // TODO: cleanup hook? } + + /** + * @private + * Read the next Item in a Decoder and fill this Item with the read data. + * + * This is called when data is received from a remote peer. + * + * @param {Y} y The Yjs instance that this Item belongs to. + * @param {BinaryDecoder} decoder The decoder object to read data from. + */ _fromBinary (y, decoder) { const missing = super._fromBinary(y, decoder) this.hookName = decoder.readVarString() return missing } + + /** + * @private + * Transform the properties of this type to binary and write it to an + * BinaryEncoder. + * + * This is called when this Item is sent to a remote peer. + * + * @param {BinaryEncoder} encoder The encoder to write data to. + */ _toBinary (encoder) { super._toBinary(encoder) encoder.writeVarString(this.hookName) } + + /** + * @private + * Integrate this type into the Yjs instance. + * + * * Save this struct in the os + * * This type is sent to other client + * * Observer functions are fired + * + * @param {Y} y The Yjs instance + */ _integrate (y) { if (this.hookName === null) { throw new Error('hookName must be defined!') diff --git a/src/Type/y-xml/YXmlText.js b/src/Type/y-xml/YXmlText.js index 7fbd48d5..0e4eb78c 100644 --- a/src/Type/y-xml/YXmlText.js +++ b/src/Type/y-xml/YXmlText.js @@ -1,5 +1,11 @@ import YText from '../YText.js' +/** + * Represents text in a Dom Element. In the future this type will also handle + * simple formatting information like bold and italic. + * + * @param {String} arg1 Initial value. + */ export default class YXmlText extends YText { constructor (arg1) { let dom = null @@ -56,6 +62,15 @@ export default class YXmlText extends YText { enableSmartScrolling (scrollElement) { this._scrollElement = scrollElement } + + /** + * @private + * Set Dom element / Text Node that represents the same content as this + * YXmlElement. + * + * @param {Element} dom The Dom Element / Text Node that is set to be + * equivalent to this Type. + */ _setDom (dom) { if (this._dom != null) { this._unbindFromDom() @@ -67,6 +82,10 @@ export default class YXmlText extends YText { this._dom = dom dom._yxml = this } + + /** + * Returns the Dom representation of this YXmlText. + */ getDom (_document) { _document = _document || document if (this._dom === null) { @@ -76,10 +95,24 @@ export default class YXmlText extends YText { } return this._dom } + + /** + * @private + * Mark this Item as deleted. + * + * @param {Y} y The Yjs instance + * @param {boolean} createDelete Whether to propagate a message that this + * Type was deleted. + */ _delete (y, createDelete) { this._unbindFromDom() super._delete(y, createDelete) } + + /** + * @private + * Unbind this YXmlText from the Dom. + */ _unbindFromDom () { if (this._domObserver != null) { this._domObserver.disconnect() diff --git a/src/Type/y-xml/utils.js b/src/Type/y-xml/utils.js index 0e1b23d0..0f30647c 100644 --- a/src/Type/y-xml/utils.js +++ b/src/Type/y-xml/utils.js @@ -184,8 +184,8 @@ export function reflectChangesOnDom (events, _document) { dom.setAttribute(attributeName, value) } }) - /** - * TODO: instead of chard-checking the types, it would be best to + /* + * TODO: instead of hard-checking the types, it would be best to * specify the type's features. E.g. * - _yxmlHasAttributes * - _yxmlHasChildren diff --git a/src/Util/EventHandler.js b/src/Util/EventHandler.js index ab3dbe4c..c82c0f46 100644 --- a/src/Util/EventHandler.js +++ b/src/Util/EventHandler.js @@ -1,22 +1,56 @@ +/** + * General event handler implementation. + */ export default class EventHandler { constructor () { this.eventListeners = [] } + + /** + * To prevent memory leaks, call this method when the eventListeners won't be + * used anymore. + */ destroy () { this.eventListeners = null } + + /** + * Adds an event listener that is called when + * {@link EventHandler#callEventListeners} is called. + * + * @param {Function} f The event handler. + */ addEventListener (f) { this.eventListeners.push(f) } + + /** + * Removes an event listener. + * + * @param {Function} f The event handler that was added with + * {@link EventHandler#addEventListener} + */ removeEventListener (f) { this.eventListeners = this.eventListeners.filter(function (g) { return f !== g }) } + + /** + * Removes all event listeners. + */ removeAllEventListeners () { this.eventListeners = [] } + + /** + * Call all event listeners that were added via + * {@link EventHandler#addEventListener}. + * + * @param {Transaction} transaction The transaction object // TODO: do we need this? + * @param {YEvent} event An event object that describes the change on a type. + */ callEventListeners (transaction, event) { for (var i = 0; i < this.eventListeners.length; i++) { try { diff --git a/src/Util/NamedEventHandler.js b/src/Util/NamedEventHandler.js index 632d8321..8eee778c 100644 --- a/src/Util/NamedEventHandler.js +++ b/src/Util/NamedEventHandler.js @@ -1,8 +1,19 @@ + +/** + * Handles named events. + */ export default class NamedEventHandler { constructor () { this._eventListener = new Map() this._stateListener = new Map() } + + /** + * @private + * Returns all listeners that listen to a specified name. + * + * @param {String} name The query event name. + */ _getListener (name) { let listeners = this._eventListener.get(name) if (listeners === undefined) { @@ -14,14 +25,34 @@ export default class NamedEventHandler { } return listeners } + + /** + * Adds a named event listener. The listener is removed after it has been + * called once. + * + * @param {String} name The event name to listen to. + * @param {Function} f The function that is executed when the event is fired. + */ once (name, f) { let listeners = this._getListener(name) listeners.once.add(f) } + + /** + * Adds a named event listener. + * + * @param {String} name The event name to listen to. + * @param {Function} f The function that is executed when the event is fired. + */ on (name, f) { let listeners = this._getListener(name) listeners.on.add(f) } + + /** + * @private + * Init the saved state for an event name. + */ _initStateListener (name) { let state = this._stateListener.get(name) if (state === undefined) { @@ -33,9 +64,20 @@ export default class NamedEventHandler { } return state } + + /** + * Returns a Promise that is resolved when the event name is called. + * The Promise is immediately resolved when the event name was called in the + * past. + */ when (name) { return this._initStateListener(name).promise } + + /** + * Remove an event listener that was registered with either + * {@link EventHandler#on} or {@link EventHandler#once}. + */ off (name, f) { if (name == null || f == null) { throw new Error('You must specify event name and function!') @@ -46,6 +88,14 @@ export default class NamedEventHandler { listener.once.delete(f) } } + + /** + * Emit a named event. All registered event listeners that listen to the + * specified name will receive the event. + * + * @param {String} name The event name. + * @param {Array} args The arguments that are applied to the event listener. + */ emit (name, ...args) { this._initStateListener(name).resolve() const listener = this._eventListener.get(name) diff --git a/src/Util/UndoManager.js b/src/Util/UndoManager.js index c18c36d3..963b45d5 100644 --- a/src/Util/UndoManager.js +++ b/src/Util/UndoManager.js @@ -64,7 +64,15 @@ function applyReverseOperation (y, scope, reverseBuffer) { return performedUndo } +/** + * Saves a history of locally applied operations. The UndoManager handles the + * undoing and redoing of locally created changes. + */ export default class UndoManager { + /** + * @param {YType} scope The scope on which to listen for changes. + * @param {Object} options Optionally provided configuration. + */ constructor (scope, options = {}) { this.options = options options.captureTimeout = options.captureTimeout == null ? 500 : options.captureTimeout @@ -101,12 +109,20 @@ export default class UndoManager { } }) } + + /** + * Undo the last locally created change. + */ undo () { this._undoing = true const performedUndo = applyReverseOperation(this.y, this._scope, this._undoBuffer) this._undoing = false return performedUndo } + + /** + * Redo the last locally created change. + */ redo () { this._redoing = true const performedRedo = applyReverseOperation(this.y, this._scope, this._redoBuffer) diff --git a/src/Util/YEvent.js b/src/Util/YEvent.js index aed8c4e8..1af22be4 100644 --- a/src/Util/YEvent.js +++ b/src/Util/YEvent.js @@ -1,9 +1,27 @@ +/** + * YEvent describes the changes on a YType. + */ export default class YEvent { + /** + * @param {YType} target The changed type. + */ constructor (target) { this.target = target this.currentTarget = target } + + /** + * Computes the path from `y` to the changed type. + * + * The following property holds: + * @example + * let type = y + * event.path.forEach(function (dir) { + * type = type.get(dir) + * }) + * type === event.target // => true + */ get path () { const path = [] let type = this.target diff --git a/src/Util/relativePosition.js b/src/Util/relativePosition.js index 77118b7f..cd824b67 100644 --- a/src/Util/relativePosition.js +++ b/src/Util/relativePosition.js @@ -1,7 +1,43 @@ import ID from './ID.js' import RootID from './RootID.js' +/** + * A relative position that is based on the Yjs model. In contrast to an + * absolute position (position by index), the relative position can be + * recomputed when remote changes are received. For example: + * + * ```Insert(0, 'x')('a|bc') = 'xa|bc'``` Where | is the cursor position. + * + * A relative cursor position can be obtained with the function + * {@link getRelativePosition} and it can be transformed to an absolute position + * with {@link fromRelativePosition}. + * + * Pro tip: Use this to implement shared cursor locations in YText or YXml! + * The relative position is {@link encodable}, so you can send it to other + * clients. + * + * @example + * // Current cursor position is at position 10 + * let relativePosition = getRelativePosition(yText, 10) + * // modify yText + * yText.insert(0, 'abc') + * yText.delete(3, 10) + * // Compute the cursor position + * let absolutePosition = fromRelativePosition(y, relativePosition) + * absolutePosition.type // => yText + * console.log('cursor location is ' + absolutePosition.offset) // => cursor location is 3 + * + * @typedef {encodable} RelativePosition + */ + +/** + * Create a relativePosition based on a absolute position. + * + * @param {YType} type The base type (e.g. YText or YArray). + * @param {Integer} offset The absolute position. + */ export function getRelativePosition (type, offset) { + // TODO: rename to createRelativePosition let t = type._start while (t !== null) { if (t._deleted === false) { @@ -15,6 +51,20 @@ export function getRelativePosition (type, offset) { return ['endof', type._id.user, type._id.clock || null, type._id.name || null, type._id.type || null] } +/** + * @typedef {Object} AbsolutePosition The result of {@link fromRelativePosition} + * @property {YType} type The type on which to apply the absolute position. + * @property {Integer} offset The absolute offset.r + */ + +/** + * Transforms a relative position back to a relative position. + * + * @param {Y} y The Yjs instance in which to query for the absolute position. + * @param {RelativePosition} rpos The relative position. + * @return {AbsolutePosition} The absolute position in the Yjs model + * (type + offset). + */ export function fromRelativePosition (y, rpos) { if (rpos[0] === 'endof') { let id diff --git a/src/Y.js b/src/Y.js index fd29a6e9..55ff5cdc 100644 --- a/src/Y.js +++ b/src/Y.js @@ -27,9 +27,32 @@ import QuillBinding from './Binding/QuillBinding.js' import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js' +/** + * Anything that can be encoded with `JSON.stringify` and can be decoded with + * `JSON.parse`. + * + * The following property should hold: + * `JSON.parse(JSON.stringify(key))===key` + * + * At the moment the only safe values are number and string. + * + * @typedef {(number|string)} encodable + */ + +/** + * A Yjs instance handles the state of shared data. + * + * @param {string} room Users in the same room share the same content + * @param {Object} opts Connector definition + * @param {AbstractPersistence} persistence Persistence adapter instance + */ export default class Y extends NamedEventHandler { constructor (room, opts, persistence) { super() + /** + * The room name that this Yjs instance connects to. + * @type {String} + */ this.room = room if (opts != null) { opts.connector.room = room @@ -37,6 +60,7 @@ export default class Y extends NamedEventHandler { this._contentReady = false this._opts = opts this.userID = generateUserID() + // TODO: This should be a Map so we can use encodables as keys this.share = {} this.ds = new DeleteStore(this) this.os = new OperationStore(this) @@ -44,6 +68,10 @@ export default class Y extends NamedEventHandler { this._missingStructs = new Map() this._readyToIntegrate = [] this._transaction = null + /** + * The {@link AbstractConnector}.that is used by this Yjs instance. + * @type {AbstractConnector} + */ this.connector = null this.connected = false let initConnection = () => { @@ -53,11 +81,15 @@ export default class Y extends NamedEventHandler { this.emit('connectorReady') } } + /** + * The {@link AbstractPersistence} that is used by this Yjs instance. + * @type {AbstractPersistence} + */ + this.persistence = null if (persistence != null) { this.persistence = persistence persistence._init(this).then(initConnection) } else { - this.persistence = null initConnection() } } @@ -77,6 +109,14 @@ export default class Y extends NamedEventHandler { } } _beforeChange () {} + /** + * Changes that happen inside of a transaction are bundled. This means that + * the observer fires _after_ the transaction is finished and that all changes + * that happened inside of the transaction are sent as one message to the + * other peers. + * + * @param {Function} f The function that should be executed as a transaction + */ transact (f, remote = false) { let initialCall = this._transaction === null if (initialCall) { @@ -117,13 +157,55 @@ export default class Y extends NamedEventHandler { this.emit('afterTransaction', this, transaction, remote) } } - // fake _start for root properties (y.set('name', type)) + + /** + * @private + * Fake _start for root properties (y.set('name', type)) + */ get _start () { return null } + + /** + * @private + * Fake _start for root properties (y.set('name', type)) + */ set _start (start) { return null } + + /** + * Define a shared data type. + * + * Multiple calls of `y.define(name, TypeConstructor)` yield the same result + * and do not overwrite each other. I.e. + * `y.define(name, type) === y.define(name, type)` + * + * After this method is called, the type is also available on `y.share[name]`. + * + * *Best Practices:* + * Either define all types right after the Yjs instance is created or always + * use `y.define(..)` when accessing a type. + * + * @example + * // Option 1 + * const y = new Y(..) + * y.define('myArray', YArray) + * y.define('myMap', YMap) + * // .. when accessing the type use y.share[name] + * y.share.myArray.insert(..) + * y.share.myMap.set(..) + * + * // Option2 + * const y = new Y(..) + * // .. when accessing the type use `y.define(..)` + * y.define('myArray', YArray).insert(..) + * y.define('myMap', YMap).set(..) + * + * @param {String} name + * @param {YType Constructor} TypeConstructor The constructor of the type definition + * @returns {YType} The created type + */ define (name, TypeConstructor) { let id = new RootID(name, TypeConstructor) let type = this.os.get(id) @@ -134,9 +216,23 @@ export default class Y extends NamedEventHandler { } return type } + + /** + * Get a defined type. The type must be defined locally. First define the + * type with {@link define}. + * + * This returns the same value as `y.share[name]` + * + * @param {String} name The typename + */ get (name) { return this.share[name] } + + /** + * Disconnect this Yjs Instance from the network. The connector will + * unsubscribe from the room and document updates are not shared anymore. + */ disconnect () { if (this.connected) { this.connected = false @@ -145,6 +241,10 @@ export default class Y extends NamedEventHandler { return Promise.resolve() } } + + /** + * If disconnected, tell the connector to reconnect to the room. + */ reconnect () { if (!this.connected) { this.connected = true @@ -153,6 +253,11 @@ export default class Y extends NamedEventHandler { return Promise.resolve() } } + + /** + * Disconnect from the room, and destroy all traces of this Yjs instance. + * Persisted data will remain until removed by the persistence adapter. + */ destroy () { super.destroy() this.share = null @@ -171,7 +276,9 @@ export default class Y extends NamedEventHandler { this.ds = null this.ss = null } + whenSynced () { + // TODO: remove this method return new Promise(resolve => { this.once('synced', () => { resolve()