├── docs ├── fonts │ ├── Lato-Regular.ttf │ ├── SourceCodePro-Regular.ttf │ └── OFL.txt ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── lang-css.js │ │ ├── Apache-License-2.0.txt │ │ └── prettify.js ├── demo │ ├── dist │ │ ├── bind-to-element.js │ │ ├── bind-to-element.js.map │ │ ├── index.js │ │ └── index.js.map │ └── dom │ │ └── random-user.html ├── styles │ ├── prettify-jsdoc.css │ ├── prettify-tomorrow.css │ └── jsdoc-default.css ├── index.js.html ├── unwatch.js.html ├── watch.js.html ├── dom_bindToElement.js.html ├── index.html ├── convert.js.html ├── global.html ├── oculusx.module_DOM.html └── module-oculusx.html ├── src ├── oculusx.js ├── index.js ├── unwatch.js ├── watch.js ├── dom │ └── bindToElement.js └── convert.js ├── .semaphore └── semaphore.yml ├── .npmignore ├── .jsdoc.json ├── test ├── scope.test.js ├── memoryleaks.test.js ├── watch.test.js ├── bind-to-element.test.js └── unwatch.test.js ├── LICENSE ├── .gitignore ├── package.json ├── demo └── dom │ └── random-user.html └── README.md /docs/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eavichay/oculusx/HEAD/docs/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eavichay/oculusx/HEAD/docs/fonts/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /src/oculusx.js: -------------------------------------------------------------------------------- 1 | const {watch} = require('./watch'); 2 | const {unwatch} = require('./unwatch'); 3 | 4 | module.exports = { 5 | watch, 6 | unwatch 7 | }; -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: First pipeline example 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu1804 7 | blocks: 8 | - name: "Test" 9 | task: 10 | jobs: 11 | - name: Test 12 | commands: 13 | - checkout 14 | - "npm install" -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | .idea 7 | .vscdoe 8 | 9 | out 10 | docs 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | lib-cov 18 | coverage 19 | .nyc_output 20 | .grunt 21 | bower_components 22 | .lock-wscript 23 | build/Release 24 | node_modules/ 25 | jspm_packages/ 26 | typings/ 27 | README.md 28 | typings.json 29 | webpack.config.js 30 | test 31 | .npm 32 | .eslintcache 33 | .node_repl_history 34 | *.tgz 35 | .yarn-integrity 36 | .env 37 | .next -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const {watch, unwatch} = require('./oculusx'); 2 | 3 | /** 4 | * @module oculusx 5 | * @description

Usage

<script src="/path/to/oculusx.js"></script> 6 | * 7 | */ 8 | module.exports = { 9 | /** 10 | * @param {object} target 11 | * @param {string} [path] 12 | * @param {Function} [callback] 13 | * @param {boolean} [invoke] 14 | * @returns {Observe} 15 | */ 16 | watch, 17 | 18 | /** 19 | * @param {object} target 20 | * @param {string} [path] 21 | * @param {Function} [callback] 22 | * @returns {Unobserve} scoped unwatch function on the target object 23 | */ 24 | unwatch 25 | }; 26 | -------------------------------------------------------------------------------- /src/unwatch.js: -------------------------------------------------------------------------------- 1 | const {convert, destroy} = require('./convert'); 2 | 3 | /** 4 | * @typedef {Function} Unobserve 5 | * @param {string} path 6 | * @param {Function} callback 7 | */ 8 | 9 | /** 10 | * @module oculusx 11 | */ 12 | module.exports = { 13 | /** 14 | * 15 | * @param {object} target 16 | * @param {string} [path] 17 | * @param {Function} [callback] 18 | * @returns {Unobserve} scoped unwatch function on the target object 19 | */ 20 | unwatch: (target, path, callback) => { 21 | if (!path) { 22 | return destroy(target); 23 | } 24 | const {unobserve} = convert(target); 25 | return unobserve(path, callback) 26 | } 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": [ 8 | "src", 9 | "package.json", 10 | "README.md" 11 | ], 12 | "includePattern": ".js", 13 | "excludePattern": "(node_modules/|docs)" 14 | }, 15 | "plugins": [ 16 | "plugins/markdown" 17 | ], 18 | "templates": { 19 | "cleverLinks": true, 20 | "monospaceLinks": true, 21 | "defaults": { 22 | "title": "oculusx" 23 | } 24 | }, 25 | "opts": { 26 | "destination": "./docs/", 27 | "encoding": "utf8", 28 | "private": false, 29 | "recurse": true, 30 | "template": "./node_modules/latodoc" 31 | } 32 | } -------------------------------------------------------------------------------- /docs/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /src/watch.js: -------------------------------------------------------------------------------- 1 | const {convert} = require('./convert'); 2 | 3 | /** 4 | * @typedef {Function} Observe 5 | * @param {string} path 6 | * @param {Function} callback 7 | * @param {boolean} [invoke = false] 8 | */ 9 | 10 | /** 11 | * @module oculusx 12 | */ 13 | module.exports = { 14 | /** 15 | * Watches for object changes 16 | * @param {object} target 17 | * @param {string} [path] 18 | * @param {Function} [callback] 19 | * @param {boolean} [invoke = false] 20 | * @returns {Observe} watch function scoped to target object only 21 | * 22 | */ 23 | watch: (target, path = undefined, callback = undefined, invoke = false) => { 24 | const {observe} = convert(target); 25 | if (path && callback) { 26 | observe(path, callback, invoke); 27 | } 28 | return observe; 29 | } 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/demo/dist/bind-to-element.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;ix;function bindToElement(model,node,path,options={}){const compute=options.compute||echo;const binder=value=>{switch(options.method){case"attribute":node.setAttribute(options.attribute,compute(value));break;default:case"text":node.textContent=compute(value);break}if(options.callback){options.callback(value)}};watch(model,path,binder,true);return()=>unwatch(model,path,binder)}oculusx.bindToElement=bindToElement;module.exports={bindToElement:bindToElement}},{}]},{},[1]); 2 | //# sourceMappingURL=true -------------------------------------------------------------------------------- /test/scope.test.js: -------------------------------------------------------------------------------- 1 | const {watch} = require('../src'); 2 | const assert = require('assert'); 3 | 4 | describe('Scope', () => { 5 | 6 | it('Should not be scoped to the target object', done => { 7 | const target = { 8 | a: 1, 9 | b: 2, 10 | c: { 11 | value: 3 12 | } 13 | }; 14 | function assertScope () { 15 | // @ts-ignore 16 | const self = this; 17 | assert.notStrictEqual(self, target); 18 | done(); 19 | } 20 | 21 | watch(target, 'c', assertScope); 22 | 23 | target.c = { 24 | value: 4 25 | }; 26 | }); 27 | 28 | it('Should not be scoped to the target object for arrow functions', done => { 29 | const target = { 30 | a: 1, 31 | b: 2, 32 | c: { 33 | value: 3 34 | } 35 | }; 36 | const assertScope = () => { 37 | // @ts-ignore 38 | const self = this; 39 | assert.notStrictEqual(self, target); 40 | done(); 41 | }; 42 | 43 | watch(target, 'c', assertScope); 44 | 45 | target.c = { 46 | value: 4 47 | }; 48 | }); 49 | 50 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Avichay Eyal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | .cache 9 | 10 | out 11 | 12 | # IDE 13 | .idea 14 | .vscdoe 15 | 16 | # Build 17 | dist 18 | !docs/demo/dist 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | 71 | # next.js build output 72 | .next 73 | -------------------------------------------------------------------------------- /test/memoryleaks.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const {unwatch, watch} = require('../src/index.js'); 3 | 4 | const iterate = (callback) => { 5 | for (let i = 0; i < 1000; i++) { 6 | callback(i); 7 | global.gc(); 8 | } 9 | }; 10 | 11 | describe('Memory leaks', function () { 12 | it('Should not leak', (done) => { 13 | const startTestHeap = process.memoryUsage().heapUsed; 14 | iterate((count) => { 15 | const target = { 16 | a: { 17 | b: { 18 | c: 3 19 | } 20 | }, 21 | x: 0 22 | }; 23 | const callback = () => target.x++; 24 | 25 | watch(target, 'a.b.c', callback); 26 | 27 | target.a.b.c = 5; 28 | target.a.b = { 29 | c: 7 30 | }; 31 | target.a = { 32 | b: { 33 | c: 15 34 | } 35 | }; 36 | }); 37 | setTimeout(() => { 38 | global.gc(); 39 | const endTestHeap = process.memoryUsage().heapUsed; 40 | assert.ok(endTestHeap <= startTestHeap, `Memory heap test failed: got ${endTestHeap - startTestHeap}. Should be zero or less`); 41 | done(); 42 | }, 100); 43 | }); 44 | }); -------------------------------------------------------------------------------- /docs/demo/dist/bind-to-element.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["dist/bind-to-element.js"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","1","module","watch","unwatch","oculusx","echo","x","bindToElement","model","node","path","options","compute","binder","value","method","setAttribute","attribute","textContent","callback"],"mappings":"CAAA,WAAY,SAASA,EAAEC,EAAEC,EAAEC,GAAG,SAASC,EAAEC,EAAEC,GAAG,IAAIJ,EAAEG,GAAG,CAAC,IAAIJ,EAAEI,GAAG,CAAC,IAAIE,EAAE,mBAAmBC,SAASA,QAAQ,IAAIF,GAAGC,EAAE,OAAOA,EAAEF,GAAG,GAAG,GAAGI,EAAE,OAAOA,EAAEJ,GAAG,GAAG,IAAIK,EAAE,IAAIC,MAAM,uBAAuBN,EAAE,KAAK,MAAMK,EAAEE,KAAK,mBAAmBF,EAAE,IAAIG,EAAEX,EAAEG,IAAIS,YAAYb,EAAEI,GAAG,GAAGU,KAAKF,EAAEC,QAAQ,SAASd,GAAG,IAAIE,EAAED,EAAEI,GAAG,GAAGL,GAAG,OAAOI,EAAEF,GAAGF,IAAIa,EAAEA,EAAEC,QAAQd,EAAEC,EAAEC,EAAEC,GAAG,OAAOD,EAAEG,GAAGS,QAAQ,IAAI,IAAIL,EAAE,mBAAmBD,SAASA,QAAQH,EAAE,EAAEA,EAAEF,EAAEa,OAAOX,IAAID,EAAED,EAAEE,IAAI,OAAOD,EAAE,OAAOJ,GAAtc,EAAA,EAA6ciB,GAAG,SAAST,QAAQU,OAAOJ,SAMxe,MAAMK,MAACA,MAAKC,QAAEA,SAAWC,QAEzB,MAAMC,KAAOC,GAAKA,EAwBlB,SAASC,cAAeC,MAAOC,KAAMC,KAAMC,YACvC,MAAMC,QAAUD,QAAQC,SAAWP,KACnC,MAAMQ,OAAUC,QACZ,OAAQH,QAAQI,QACZ,IAAK,YACDN,KAAKO,aAAaL,QAAQM,UAAWL,QAAQE,QAC7C,MACJ,QACA,IAAK,OACDL,KAAKS,YAAcN,QAAQE,OAC3B,MAER,GAAIH,QAAQQ,SAAU,CAClBR,QAAQQ,SAASL,SAGzBZ,MAAMM,MAAOE,KAAMG,OAAQ,MAC3B,MAAO,IAAMV,QAAQK,MAAOE,KAAMG,QAGtCT,QAAQG,cAAgBA,cAGxBN,OAAOJ,SACHU,cAAAA,yBAGO"} -------------------------------------------------------------------------------- /src/dom/bindToElement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module oculusx.DOM 3 | * @requires oculusx 4 | */ 5 | 6 | const {watch, unwatch} = oculusx; 7 | 8 | const echo = x => x; 9 | 10 | /** 11 | * @typedef NodeBindingOptions 12 | * @property {'attribute'|'text'} [method = 'text'] 13 | * @property {string} [attribute] 14 | * @property {Function} [compute] 15 | */ 16 | 17 | /** 18 | * @param {object} model 19 | * @param {Element} node 20 | * @param {string} path 21 | * @param {NodeBindingOptions} options 22 | * @returns {Function} function to unbind the defined binding 23 | * 24 | * @example 25 | * const element = document.getElementById('#my-element'); 26 | * const model = {}; 27 | * const unsubscribe = oculusx.bindToElement(model, element, 'path.to.watch'); 28 | * model.path = { to: watch: 12345 }; // DOM node changes 29 | * unsubscribe(); 30 | * model.path.to.watch = 'Hello'; // DOM node does not change 31 | */ 32 | function bindToElement (model, node, path, options = {}) { 33 | const compute = options.compute || echo; 34 | const binder = (value) => { 35 | switch (options.method) { 36 | case 'attribute': 37 | node.setAttribute(options.attribute, compute(value)); 38 | break; 39 | default: 40 | case 'text': 41 | node.textContent = compute(value); 42 | break; 43 | } 44 | if (options.callback) { 45 | options.callback(value); 46 | } 47 | }; 48 | watch(model, path, binder, true); 49 | return () => unwatch(model, path, binder); 50 | } 51 | 52 | oculusx.bindToElement = bindToElement; 53 | 54 | 55 | module.exports = { 56 | bindToElement 57 | }; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oculusx", 3 | "version": "0.4.1", 4 | "description": "Upgrade objects to observables", 5 | "main": "src/index.js", 6 | "browser": "dist/index.js", 7 | "scripts": { 8 | "test": "node ./node_modules/.bin/mocha --expose-gc --timeout 5000 test/**/**.test.js", 9 | "build": "rm -rf ./dist && npm test && npm run build:core && npm run build:dom && npm run build:docs", 10 | "build:docs": "rm -rf docs && jsdoc ./README.md src -r -t node_modules/latodoc -d docs && cp -R demo ./docs && cp -R dist ./docs/demo", 11 | "build:core": "browserify ./src/oculusx.js --outfile dist/index.js -s oculusx && uglifyjs dist/index.js -o dist/index.js --source-map url", 12 | "build:dom": "browserify ./src/dom/bindToElement.js --outfile dist/bind-to-element.js && uglifyjs dist/bind-to-element.js -o dist/bind-to-element.js --source-map url", 13 | "postbuild": "uglifyjs dist/index.js -o dist/index.js --source-map url", 14 | "prepublish": "npm test && npm run build" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/eavichay/ocolus.git" 19 | }, 20 | "keywords": [ 21 | "Observables", 22 | "Javascript", 23 | "State-Management" 24 | ], 25 | "author": "Avichay Eyal ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/eavichay/ocolus/issues" 29 | }, 30 | "globals": [ 31 | "oculusx" 32 | ], 33 | "homepage": "https://github.com/eavichay/ocolus#readme", 34 | "devDependencies": { 35 | "browserify": "16.2.3", 36 | "jsdoc": "^3.5.5", 37 | "latodoc": "github:smeijer/latodoc", 38 | "mocha": "5.2.0", 39 | "nyc": "13.3.0", 40 | "uglify-es": "3.3.9" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: hsl(104, 100%, 24%); 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /test/watch.test.js: -------------------------------------------------------------------------------- 1 | const {watch} = require('../src'); 2 | const assert = require('assert'); 3 | 4 | describe('watch', () => { 5 | it('Should execute multiple watches', () => { 6 | const target = { 7 | a: 1234 8 | }; 9 | let count = 0; 10 | watch(target, 'a', () => count++); // #1 11 | watch(target, 'a', () => count++); // #2 12 | watch(target) 13 | ('b.c', () => count++) // #3 14 | ('b.c', () => count++) // #4 15 | ('a', () => count++) // #5 16 | ('b', () => target.a++); // #6 will trigger the three 'a' watchers (#7, #8, #9) 17 | target.a = 5; 18 | target.b = {c: 1}; 19 | assert.strictEqual(count, 9); 20 | }); 21 | it('Should observe on an existing property changes', () => { 22 | const newValues = ['world', 'gibberish', 'cucumber', 15, undefined, null, [], {}, /abc/g]; 23 | 24 | newValues.forEach(newValue => { 25 | const target = {a: 'hello'}; 26 | watch(target, 'a', (value, property) => { 27 | assert.notStrictEqual(value, newValue); 28 | assert.strictEqual(property, 'a') 29 | }) 30 | }); 31 | }); 32 | it('Should observe non-existing property changes', () => { 33 | const target = {}; 34 | watch(target) 35 | ('a', (value, property) => { 36 | assert(typeof value === 'object'); 37 | assert.strictEqual(property, 'a'); 38 | }) 39 | ('a.b', (value, property) => { 40 | assert(typeof value === typeof target.a.b); 41 | assert.strictEqual(value, target.a.b); 42 | assert.strictEqual(property, 'b'); 43 | }); 44 | 45 | target.a = {}; 46 | target.a.b = 5; 47 | }); 48 | it('Should observe non-object path that changes to object', () => { 49 | const target = { 50 | a: 1 51 | }; 52 | watch(target, 'a.b', (value, property) => { 53 | assert.strictEqual(value, 5); 54 | assert.strictEqual(property, 'b'); 55 | }); 56 | 57 | target.a = { 58 | b: 5 59 | }; 60 | }); 61 | it('Should observe changes when a property becomes undefined', done => { 62 | const target = { 63 | a: 5 64 | }; 65 | watch(target, 'a', (value) => { 66 | assert(void 0 === value); 67 | done(); 68 | }); 69 | target.a = undefined; 70 | }); 71 | it('Should observe changes inside an object that becomes undefined and then becomes an object', done => { 72 | const target = { 73 | a: { 74 | name: 'hello' 75 | } 76 | }; 77 | watch(target, 'a.name', (value) => { 78 | assert.strictEqual(value, 'again'); 79 | done(); 80 | }); 81 | target.a = undefined; 82 | target.a = { 83 | name: 'again' 84 | }; 85 | }); 86 | }); -------------------------------------------------------------------------------- /docs/index.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | index.js - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

index.js

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
const {watch, unwatch} = require('./oculusx');
42 | 
43 | /**
44 |  * @module oculusx
45 |  * @description <h2>Usage</h2> &lt;script src="/path/to/oculusx.js"&gt;&lt;/script&gt;
46 |  *
47 |  */
48 | module.exports = {
49 |   /**
50 |    * @param {object} target
51 |    * @param {string} [path]
52 |    * @param {Function} [callback]
53 |    * @param {boolean} [invoke]
54 |    * @returns {Observe}
55 |    */
56 |   watch,
57 | 
58 |   /**
59 |    * @param {object} target
60 |    * @param {string} [path]
61 |    * @param {Function} [callback]
62 |    * @returns {Unobserve} scoped unwatch function on the target object
63 |    */
64 |   unwatch
65 | };
66 | 
67 |
68 |
69 | 70 | 71 | 72 | 73 |
74 | 75 |
76 | 77 |
78 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 79 |
80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/unwatch.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | unwatch.js - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

unwatch.js

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
const {convert, destroy} = require('./convert');
42 | 
43 | /**
44 |  * @typedef {Function} Unobserve
45 |  * @param {string} path
46 |  * @param {Function} callback
47 |  */
48 | 
49 | /**
50 |  * @module oculusx
51 |  */
52 | module.exports = {
53 |   /**
54 |    *
55 |    * @param {object} target
56 |    * @param {string} [path]
57 |    * @param {Function} [callback]
58 |    * @returns {Unobserve} scoped unwatch function on the target object
59 |    */
60 |   unwatch: (target, path, callback) => {
61 |     if (!path) {
62 |       return destroy(target);
63 |     }
64 |     const {unobserve} = convert(target);
65 |     return unobserve(path, callback)
66 |   }
67 | };
68 | 
69 | 
70 |
71 |
72 | 73 | 74 | 75 | 76 |
77 | 78 |
79 | 80 |
81 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 82 |
83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /docs/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: hsl(104, 100%, 24%); } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: hsl(240, 100%, 50%); } 17 | 18 | /* a comment */ 19 | .com { 20 | color: hsl(0, 0%, 60%); } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: hsl(240, 100%, 32%); } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: hsl(240, 100%, 40%); } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #000000; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #000000; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #000000; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /docs/watch.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | watch.js - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

watch.js

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
const {convert} = require('./convert');
42 | 
43 | /**
44 |  * @typedef {Function} Observe
45 |  * @param {string} path
46 |  * @param {Function} callback
47 |  * @param {boolean} [invoke = false]
48 |  */
49 | 
50 | /**
51 |  * @module oculusx
52 |  */
53 | module.exports = {
54 |   /**
55 |    * Watches for object changes
56 |    * @param {object} target
57 |    * @param {string} [path]
58 |    * @param {Function} [callback]
59 |    * @param {boolean} [invoke = false]
60 |    * @returns {Observe} watch function scoped to target object only
61 |    *
62 |    */
63 |   watch: (target, path = undefined, callback = undefined, invoke = false) => {
64 |     const {observe} = convert(target);
65 |     if (path && callback) {
66 |       observe(path, callback, invoke);
67 |     }
68 |     return observe;
69 |   }
70 | };
71 | 
72 | 
73 |
74 |
75 | 76 | 77 | 78 | 79 |
80 | 81 |
82 | 83 |
84 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 85 |
86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /demo/dom/random-user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | User Editor - oculusx 6 | 7 | 8 | 9 | 22 | 23 | 24 |
25 |

oculusx: DOM binding demo

26 |

Simple model -> view binding with oculusx

27 | User data credit: https://randomuser.me 28 |
29 | 41 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/demo/dom/random-user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | User Editor - oculusx 6 | 7 | 8 | 9 | 22 | 23 | 24 |
25 |

oculusx: DOM binding demo

26 |

Simple model -> view binding with oculusx

27 | User data credit: https://randomuser.me 28 |
29 | 41 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/dom_bindToElement.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dom/bindToElement.js - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

dom/bindToElement.js

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
/**
 42 |  * @module oculusx.DOM
 43 |  * @requires oculusx
 44 |  */
 45 | 
 46 | const {watch, unwatch} = oculusx;
 47 | 
 48 | const echo = x => x;
 49 | 
 50 | /**
 51 |  * @typedef NodeBindingOptions
 52 |  * @property {'attribute'|'text'} [method = 'text']
 53 |  * @property {string} [attribute]
 54 |  * @property {Function} [compute]
 55 |  */
 56 | 
 57 | /**
 58 |  * @param {object} model
 59 |  * @param {Element} node
 60 |  * @param {string} path
 61 |  * @param {NodeBindingOptions} options
 62 |  * @returns {Function} function to unbind the defined binding
 63 |  *
 64 |  * @example
 65 |  * const element = document.getElementById('#my-element');
 66 |  * const model = {};
 67 |  * const unsubscribe = oculusx.bindToElement(model, element, 'path.to.watch');
 68 |  * model.path = { to: watch: 12345 }; // DOM node changes
 69 |  * unsubscribe();
 70 |  * model.path.to.watch = 'Hello'; // DOM node does not change
 71 |  */
 72 | function bindToElement (model, node, path, options = {}) {
 73 |     const compute = options.compute || echo;
 74 |     const binder = (value) => {
 75 |         switch (options.method) {
 76 |             case 'attribute':
 77 |                 node.setAttribute(options.attribute, compute(value));
 78 |                 break;
 79 |             default:
 80 |             case 'text':
 81 |                 node.textContent = compute(value);
 82 |                 break;
 83 |         }
 84 |         if (options.callback) {
 85 |             options.callback(value);
 86 |         }
 87 |     };
 88 |     watch(model, path, binder, true);
 89 |     return () => unwatch(model, path, binder);
 90 | }
 91 | 
 92 | oculusx.bindToElement = bindToElement;
 93 | 
 94 | 
 95 | module.exports = {
 96 |     bindToElement
 97 | };
 98 | 
99 |
100 |
101 | 102 | 103 | 104 | 105 |
106 | 107 |
108 | 109 |
110 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 111 |
112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /test/bind-to-element.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | global.oculusx = require('../src/index.js'); 3 | require('../src/dom/bindToElement.js'); 4 | 5 | describe('bind-to-element', function () { 6 | let count = 0; 7 | const {bindToElement} = oculusx; 8 | it('should bind a model to element\'s inner-text', function () { 9 | const mockElement = { 10 | set textContent (value) { 11 | switch (count) { 12 | case 0: 13 | assert.strictEqual('initialText', value); 14 | break; 15 | case 1: 16 | assert.strictEqual('myText', value); 17 | break; 18 | default: 19 | throw new Error('Should not occur'); 20 | } 21 | count++; 22 | } 23 | }; 24 | const model = {a: 'initialText'}; 25 | bindToElement(model, mockElement, 'a'); 26 | model.a = 'myText'; 27 | }); 28 | 29 | it('should bind a model to element\' inner-text with computed value', function () { 30 | let count = 0; 31 | const mockElement = { 32 | set textContent (value) { 33 | switch (count) { 34 | case 0: 35 | assert.strictEqual('INITIAL', value); 36 | break; 37 | case 1: 38 | assert.strictEqual('MYTEXT', value); 39 | break; 40 | default: 41 | throw new Error('Should not occur'); 42 | } 43 | count++; 44 | } 45 | }; 46 | const capitalize = x => x.toUpperCase(); 47 | const model = { 48 | a: 'initial' 49 | }; 50 | bindToElement(model, mockElement, 'a', { 51 | compute: capitalize 52 | }); 53 | model.a = 'myText'; 54 | }); 55 | 56 | it('Should set attribute', function (done) { 57 | const mockElement = { 58 | setAttribute(attr, value) { 59 | assert.strictEqual(attr, 'some-attr'); 60 | assert.strictEqual(value, 'some-value'); 61 | done(); 62 | } 63 | }; 64 | const model = { 65 | a: 'some-value' 66 | }; 67 | bindToElement(model, mockElement, 'a', { 68 | method: 'attribute', 69 | attribute: 'some-attr' 70 | }); 71 | }); 72 | 73 | it('Should set attribute - computed', function (done) { 74 | const mockElement = { 75 | setAttribute(attr, value) { 76 | assert.strictEqual(attr, 'some-attr'); 77 | assert.strictEqual(value, 'some-value'); 78 | done(); 79 | } 80 | }; 81 | const model = { 82 | a: 'eulav-emos' 83 | }; 84 | bindToElement(model, mockElement, 'a', { 85 | method: 'attribute', 86 | attribute: 'some-attr', 87 | compute: x => x.split('').reverse().join('') 88 | }); 89 | }); 90 | 91 | it('should unsubscribe', function (done) { 92 | let counter = 0; 93 | const mockElement = { 94 | set textContent (value) { 95 | assert.equal(value, counter === 0 ? undefined : counter === 1 ? 'Hello' : new Error('Ooops')) 96 | counter++; 97 | if (counter > 2) { 98 | done(new Error('Should be only invoked once')); 99 | } 100 | } 101 | }; 102 | const mockData = {}; 103 | const unsub = bindToElement(mockData, mockElement, 'a'); 104 | mockData.a = 'Hello'; 105 | unsub(); 106 | mockData.a = 'Bello'; 107 | done(); 108 | }); 109 | }); -------------------------------------------------------------------------------- /docs/demo/dist/index.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.oculusx=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;inoOp;const deadObservableExecution=function(){throw new Error("Cannot watch a non-registered object")};function destroy(target){const meta=registeredObservables.get(target);registeredObservables.delete(target);if(meta){meta.observe=deadObservableExecution;meta.observers.clear();meta.values={};return meta.unobserve}return noOp}function convert(target){let metadata=registeredObservables.get(target);if(metadata){return metadata}else{metadata={observers:new Map,observe:undefined,unobserve:undefined,values:{}}}const{observers:observers,values:values}=metadata;const unobserve=(path,callback)=>{if(typeof path==="undefined"){return destroy(target)}else if(path){const chain=path.split(".");const[prop]=chain;const set=observers.get(path);if(set){if(callback){set.delete(callback)}else{set.clear()}const nextInChain=target[prop];if(typeof nextInChain==="object"){const observeMethods=registeredObservables.get(nextInChain);if(observeMethods){const{unobserve:unobserve}=observeMethods;const path=chain.slice(1).join(".");if(path&&unobserve!==deadObservableExecution){unobserve(path,callback)}}}if(set.size===0){observers.delete(path)}}}return unobserve};const observe=(path,callback,invoke)=>{if(!registeredObservables.has(target)){deadObservableExecution()}const chain=path.split(".");const[prop]=chain;const isNew=!observers.has(prop);let callbacks=observers.get(path);if(!callbacks){callbacks=new Set;observers.set(path,callbacks)}callbacks.add(callback);if(isNew){const initialValue=target[prop];if(typeof initialValue==="object"){const{observe:observe}=convert(initialValue);Array.from(observers.entries()).forEach(([path,set])=>{if(path.startsWith(prop+".")){set.forEach(cb=>observe(path.split(".").slice(1).join("."),cb,invoke))}})}values[prop]=initialValue;typeof target==="object"&&Object.defineProperty(target,prop,{get(){return values[prop]},set(v){values[prop]=v;const set=observers.get(prop);set&&set.forEach(cb=>cb(v,prop));if(typeof v==="object"){const{observe:observe}=convert(v);Array.from(observers.entries()).forEach(([path,set])=>{if(path.startsWith(prop+".")){set.forEach(cb=>observe(path.split(".").slice(1).join("."),cb,true))}})}},configurable:true})}if(invoke){const set=observers.get(prop);set&&set.forEach(cb=>cb(values[prop],prop))}return observe};metadata.observe=observe;metadata.unobserve=unobserve;metadata.values=values;registeredObservables.set(target,metadata);return metadata}module.exports={convert:convert,destroy:destroy}},{}],2:[function(require,module,exports){const{watch:watch}=require("./watch");const{unwatch:unwatch}=require("./unwatch");module.exports={watch:watch,unwatch:unwatch}},{"./unwatch":3,"./watch":4}],3:[function(require,module,exports){const{convert:convert,destroy:destroy}=require("./convert");module.exports={unwatch:(target,path,callback)=>{if(!path){return destroy(target)}const{unobserve:unobserve}=convert(target);return unobserve(path,callback)}}},{"./convert":1}],4:[function(require,module,exports){const{convert:convert}=require("./convert");module.exports={watch:(target,path=undefined,callback=undefined,invoke=false)=>{const{observe:observe}=convert(target);if(path&&callback){observe(path,callback,invoke)}return observe}}},{"./convert":1}]},{},[2])(2)}); 2 | //# sourceMappingURL=true -------------------------------------------------------------------------------- /docs/demo/dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["dist/index.js"],"names":["f","exports","module","define","amd","g","window","global","self","this","oculusx","r","e","n","t","o","i","c","require","u","a","Error","code","p","call","length","1","registeredObservables","WeakMap","noOp","deadObservableExecution","destroy","target","meta","get","delete","observe","observers","clear","values","unobserve","convert","metadata","Map","undefined","path","callback","chain","split","prop","set","nextInChain","observeMethods","slice","join","size","invoke","has","isNew","callbacks","Set","add","initialValue","Array","from","entries","forEach","startsWith","cb","Object","defineProperty","[object Object]","v","configurable","2","watch","unwatch","./unwatch","./watch","3","./convert","4"],"mappings":"CAAA,SAAUA,GAAG,UAAUC,UAAU,iBAAiBC,SAAS,YAAY,CAACA,OAAOD,QAAQD,SAAS,UAAUG,SAAS,YAAYA,OAAOC,IAAI,CAACD,UAAUH,OAAO,CAAC,IAAIK,EAAE,UAAUC,SAAS,YAAY,CAACD,EAAEC,YAAY,UAAUC,SAAS,YAAY,CAACF,EAAEE,YAAY,UAAUC,OAAO,YAAY,CAACH,EAAEG,SAAS,CAACH,EAAEI,KAAKJ,EAAEK,QAAUV,MAA5T,CAAmU,WAAW,IAAIG,OAAOD,OAAOD,QAAQ,OAAO,WAAY,SAASU,EAAEC,EAAEC,EAAEC,GAAG,SAASC,EAAEC,EAAEhB,GAAG,IAAIa,EAAEG,GAAG,CAAC,IAAIJ,EAAEI,GAAG,CAAC,IAAIC,EAAE,mBAAmBC,SAASA,QAAQ,IAAIlB,GAAGiB,EAAE,OAAOA,EAAED,GAAG,GAAG,GAAGG,EAAE,OAAOA,EAAEH,GAAG,GAAG,IAAII,EAAE,IAAIC,MAAM,uBAAuBL,EAAE,KAAK,MAAMI,EAAEE,KAAK,mBAAmBF,EAAE,IAAIG,EAAEV,EAAEG,IAAIf,YAAYW,EAAEI,GAAG,GAAGQ,KAAKD,EAAEtB,QAAQ,SAASU,GAAG,IAAIE,EAAED,EAAEI,GAAG,GAAGL,GAAG,OAAOI,EAAEF,GAAGF,IAAIY,EAAEA,EAAEtB,QAAQU,EAAEC,EAAEC,EAAEC,GAAG,OAAOD,EAAEG,GAAGf,QAAQ,IAAI,IAAIkB,EAAE,mBAAmBD,SAASA,QAAQF,EAAE,EAAEA,EAAEF,EAAEW,OAAOT,IAAID,EAAED,EAAEE,IAAI,OAAOD,EAAE,OAAOJ,EAAtc,EAAA,EAA6ce,GAAG,SAASR,QAAQhB,OAAOD,SAgBv1B,MAAM0B,sBAAwB,IAAIC,QAMlC,MAAMC,KAAO,IAAMA,KAKnB,MAAMC,wBAA0B,WAC9B,MAAM,IAAIT,MAAM,yCAQlB,SAASU,QAAQC,QACf,MAAMC,KAAON,sBAAsBO,IAAIF,QACvCL,sBAAsBQ,OAAOH,QAC7B,GAAIC,KAAM,CACRA,KAAKG,QAAUN,wBACfG,KAAKI,UAAUC,QACfL,KAAKM,UACL,OAAON,KAAKO,UAEd,OAAOX,KAST,SAASY,QAAQT,QAIf,IAAIU,SAAWf,sBAAsBO,IAAIF,QACzC,GAAIU,SAAU,CACZ,OAAOA,aACF,CACLA,UACEL,UAAW,IAAIM,IACfP,QAASQ,UACTJ,UAAWI,UACXL,WAIJ,MAAMF,UAACA,UAASE,OAAEA,QAAUG,SAO5B,MAAMF,UAAY,CAACK,KAAMC,YACvB,UAAWD,OAAS,YAAa,CAC/B,OAAOd,QAAQC,aACV,GAAIa,KAAM,CACf,MAAME,MAAQF,KAAKG,MAAM,KACzB,MAAOC,MAASF,MAChB,MAAMG,IAAMb,UAAUH,IAAIW,MAC1B,GAAIK,IAAK,CACP,GAAIJ,SAAU,CACZI,IAAIf,OAAOW,cACN,CACLI,IAAIZ,QAEN,MAAMa,YAAcnB,OAAOiB,MAC3B,UAAWE,cAAgB,SAAU,CACnC,MAAMC,eAAiBzB,sBAAsBO,IAAIiB,aACjD,GAAIC,eAAgB,CAClB,MAAMZ,UAACA,WAAaY,eACpB,MAAMP,KAAOE,MAAMM,MAAM,GAAGC,KAAK,KACjC,GAAIT,MAAQL,YAAcV,wBAAyB,CACjDU,UAAUK,KAAMC,YAItB,GAAII,IAAIK,OAAS,EAAG,CAClBlB,UAAUF,OAAOU,QAKvB,OAAOL,WAQT,MAAMJ,QAAU,CAACS,KAAMC,SAAUU,UAC/B,IAAK7B,sBAAsB8B,IAAIzB,QAAS,CACtCF,0BAEF,MAAMiB,MAAQF,KAAKG,MAAM,KACzB,MAAOC,MAASF,MAChB,MAAMW,OAASrB,UAAUoB,IAAIR,MAC7B,IAAIU,UAAYtB,UAAUH,IAAIW,MAC9B,IAAKc,UAAW,CAIdA,UAAY,IAAIC,IAChBvB,UAAUa,IAAIL,KAAMc,WAEtBA,UAAUE,IAAIf,UAEd,GAAIY,MAAO,CACT,MAAMI,aAAe9B,OAAOiB,MAC5B,UAAWa,eAAiB,SAAU,CACpC,MAAM1B,QAACA,SAAWK,QAAQqB,cAC1BC,MAAMC,KAAK3B,UAAU4B,WAAWC,QAAQ,EAAErB,KAAMK,QAC9C,GAAIL,KAAKsB,WAAWlB,KAAO,KAAM,CAC/BC,IAAIgB,QAAQE,IAAMhC,QAAQS,KAAKG,MAAM,KAAKK,MAAM,GAAGC,KAAK,KAAMc,GAAIZ,YAIxEjB,OAAOU,MAAQa,oBACR9B,SAAW,UAAYqC,OAAOC,eAAetC,OAAQiB,MAC1DsB,MACE,OAAOhC,OAAOU,OAEhBsB,IAAIC,GACFjC,OAAOU,MAAQuB,EACf,MAAMtB,IAAMb,UAAUH,IAAIe,MAC1BC,KAAOA,IAAIgB,QAAQE,IAAMA,GAAGI,EAAGvB,OAC/B,UAAWuB,IAAM,SAAU,CACzB,MAAMpC,QAACA,SAAWK,QAAQ+B,GAC1BT,MAAMC,KAAK3B,UAAU4B,WAAWC,QAAQ,EAAErB,KAAMK,QAC9C,GAAIL,KAAKsB,WAAWlB,KAAO,KAAM,CAC/BC,IAAIgB,QAAQE,IAAMhC,QAAQS,KAAKG,MAAM,KAAKK,MAAM,GAAGC,KAAK,KAAMc,GAAI,YAK1EK,aAAc,OAIlB,GAAIjB,OAAQ,CACV,MAAMN,IAAMb,UAAUH,IAAIe,MAC1BC,KAAOA,IAAIgB,QAAQE,IAAMA,GAAG7B,OAAOU,MAAOA,OAG5C,OAAOb,SAGTM,SAASN,QAAUA,QACnBM,SAASF,UAAYA,UACrBE,SAASH,OAASA,OAElBZ,sBAAsBuB,IAAIlB,OAAQU,UAClC,OAAOA,SAMTxC,OAAOD,SACLwC,QAAAA,QACAV,QAAAA,cAEI2C,GAAG,SAASxD,QAAQhB,OAAOD,SACjC,MAAM0E,MAACA,OAASzD,QAAQ,WACxB,MAAM0D,QAACA,SAAW1D,QAAQ,aAE1BhB,OAAOD,SACL0E,MAAAA,MACAC,QAAAA,WAECC,YAAY,EAAEC,UAAU,IAAIC,GAAG,SAAS7D,QAAQhB,OAAOD,SAC1D,MAAMwC,QAACA,QAAOV,QAAEA,SAAWb,QAAQ,aAWnChB,OAAOD,SAQL2E,QAAS,CAAC5C,OAAQa,KAAMC,YACtB,IAAKD,KAAM,CACT,OAAOd,QAAQC,QAEjB,MAAMQ,UAACA,WAAaC,QAAQT,QAC5B,OAAOQ,UAAUK,KAAMC,cAKxBkC,YAAY,IAAIC,GAAG,SAAS/D,QAAQhB,OAAOD,SAC9C,MAAMwC,QAACA,SAAWvB,QAAQ,aAY1BhB,OAAOD,SAUL0E,MAAO,CAAC3C,OAAQa,KAAOD,UAAWE,SAAWF,UAAWY,OAAS,SAC/D,MAAMpB,QAACA,SAAWK,QAAQT,QAC1B,GAAIa,MAAQC,SAAU,CACpBV,QAAQS,KAAMC,SAAUU,QAE1B,OAAOpB,YAKR4C,YAAY,SAAS,GAlQuV,CAkQnV"} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oculusx [![Build Status](https://semaphoreci.com/api/v1/eavichay/oculusx/branches/master/badge.svg)](https://semaphoreci.com/eavichay/oculusx) 2 | 3 | Easy javascript object observing for browser and node 4 | > Oculus is a latin word for *eye* or *sight* 5 | 6 | [See Demo](./demo/dom/random-user.html) 7 | 8 | ### Watching 9 | 10 | ```javascript 11 | 12 | import { watch } from 'oculusx'; 13 | 14 | const appData = { 15 | // empty state at first, but it can be a fully populated object 16 | }; 17 | 18 | watch(appData) 19 | ('user.name', (value) => { 20 | // do something when the name changes 21 | }) 22 | ('user', (value) => { 23 | // do something when the whole user object changes 24 | // subscription to name remains and will be invoked even if user was assigned with a new object 25 | }); 26 | 27 | appData.user = { 28 | name: 'John Doe' // both watchers are invoked 29 | }; 30 | 31 | appData.user.name = 'Jane Doe'; // user.name watcher is invoked 32 | 33 | appData.user = undefined; // both watchers are invoked 34 | 35 | appData.user = { 36 | name: 'Jordan Doe' // user.name is still intact and both watchers will be invoked. 37 | }; 38 | ``` 39 | 40 | ### Unwatching a specific callback 41 | 42 | ```javascript 43 | import { watch, unwatch } from 'oculusx' 44 | 45 | const handler_1 = (value) => { 46 | // do something 47 | }; 48 | 49 | const handler_2 = (value) => { 50 | // do something 51 | }; 52 | 53 | watch(appData) 54 | ('user.name', handler_1) 55 | ('user.name', handler_2); 56 | 57 | unwatch(appData)('user.name', handler_1); 58 | 59 | appData.user = { 60 | name: 'Jaquelin Doe' // only handler_2 is invoked 61 | }; 62 | ``` 63 | 64 | ### Unwatching all callbacks from a specific path 65 | 66 | ```javascript 67 | import { watch, unwatch } from 'oculusx'; 68 | 69 | watch(appData) 70 | ('user', () => { /* do something useful */ }) // callback #1 71 | ('user', () => { /* do something useful */ }) // callback #2 72 | ('user.name', () => { /* do something useful */ }); // watch user.name 73 | 74 | unwatch(appData)('user'); // removes all user object watchers 75 | 76 | appData.user.name = 'James Doe'; // user.name watcher is invoked 77 | appData.user = { 78 | name: 'Jimmy Doe' // Only user.name watcher is invoked 79 | }; 80 | 81 | ``` 82 | 83 | ### Immediate invocation 84 | ```javascript 85 | 86 | watch(target)(path, callback, true); 87 | 88 | ``` 89 | 90 | ### Alternative syntax 91 | For those who dislike currying syntax: 92 | ```javascript 93 | watch(target) 94 | (path, callback) 95 | (path2, callback2); 96 | 97 | // equals 98 | watch(target, path, callback); 99 | watch(target, path2, callback2); 100 | 101 | // or 102 | watch(target, path, callback) 103 | (path2, callback2); 104 | ``` 105 | It also works the same for `unwatch`. 106 | 107 | ### DOM Binding - browser only feature 108 | [Demo](./demo/dom/random-user.html) 109 | #### Interface: 110 | ```ts 111 | type NodeBindingOptions = { 112 | method?: 'attribute'|'text', // defaults to 'text' 113 | attribute?: string, // attribute to be changed applicable only when method is 'attribute', 114 | compute?: Function // observed value can be computed before injected to the element 115 | }; 116 | 117 | bindToElement (model: Object, target: Element, path: string, options?:NodeBindingOptions) => Function 118 | // returns unsubscribe function to stop binding 119 | ``` 120 | 121 | #### Example: 122 | ```javascript 123 | import 'oculusx/bind-to-element'; 124 | const someElement = document.querySelector('#myElement'); 125 | const model = {} 126 | const unsubscribe = oculusx.bindToElement(model, someElement, 'path.to.data'); 127 | model.path.to = { 128 | data: 'Hello, world' 129 | }; // element's text changed to 'Hello, world' 130 | unsubscribe(); // element is no longer bound to model 131 | ``` 132 | 133 | #### Using options: 134 | ```javascript 135 | const someElement = document.querySelector('#myElement'); 136 | const unsubscribe = oculusx.bindToElement(model, someElement, 'path.to.data', { 137 | method: 'attribute', 138 | attribute: 'my-attr-name', 139 | compute: x => x.toLowerCase() 140 | }); 141 | ``` 142 | 143 | ## Future releases 144 | - Unwatching all nested pathes: `unwatch(target)('some.path.*');` 145 | - Unwatching all pathes from an object: `unwatch(target, '*');` 146 | - Watching arrays. Currently partially working only when shift/unshift are invoked or the whole array is replaced 147 | - Documentation 148 | 149 | ### Installation 150 | `npm i oculusx` 151 | `yarn add oculusx` 152 | -------------------------------------------------------------------------------- /docs/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" 2 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 3 | This license is copied below, and is also available with a FAQ at: 4 | http://scripts.sil.org/OFL 5 | 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font creation 14 | efforts of academic and linguistic communities, and to provide a free and 15 | open framework in which fonts may be shared and improved in partnership 16 | with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply 25 | to any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software components as 36 | distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, deleting, 39 | or substituting -- in part or in whole -- any of the components of the 40 | Original Version, by changing formats or by porting the Font Software to a 41 | new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 49 | redistribute, and sell modified and unmodified copies of the Font 50 | Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, 53 | in Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the corresponding 64 | Copyright Holder. This restriction only applies to the primary font name as 65 | presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created 77 | using the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /src/convert.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module oculusx 3 | * @ignore 4 | * @typedef ObservableMetadata 5 | * @property {Function|undefined} observe 6 | * @property {Function|undefined} unobserve, 7 | * @property {Map>} observers 8 | * @property {object} [values = {}] 9 | */ 10 | 11 | 12 | /** 13 | * @private 14 | * @type {WeakMap} 15 | */ 16 | const registeredObservables = new WeakMap(); 17 | 18 | /** 19 | * @private 20 | * @returns {Function} 21 | */ 22 | const noOp = () => noOp; 23 | 24 | /** 25 | * @private 26 | */ 27 | const deadObservableExecution = function () { 28 | throw new Error('Cannot watch a non-registered object'); 29 | }; 30 | 31 | /** 32 | * @private 33 | * @param {object} target 34 | * @returns {Function} 35 | */ 36 | function destroy(target) { 37 | const meta = registeredObservables.get(target); 38 | registeredObservables.delete(target); 39 | if (meta) { 40 | meta.observe = deadObservableExecution; 41 | meta.observers.clear(); 42 | meta.values = {}; 43 | return meta.unobserve; 44 | } 45 | return noOp; 46 | } 47 | 48 | /** 49 | * Enables object observing on a target object 50 | * @private 51 | * @param {object} target 52 | * @returns {ObservableMetadata} 53 | */ 54 | function convert(target) { 55 | /** 56 | * @type {ObservableMetadata} 57 | */ 58 | let metadata = registeredObservables.get(target); 59 | if (metadata) { 60 | return metadata; 61 | } else { 62 | metadata = { 63 | observers: new Map(), 64 | observe: undefined, 65 | unobserve: undefined, 66 | values: {} 67 | }; 68 | } 69 | 70 | const {observers, values} = metadata; 71 | 72 | /** 73 | * @param [path] 74 | * @param {Function} [callback] 75 | * @returns {unobserve} 76 | */ 77 | const unobserve = (path, callback) => { 78 | if (typeof path === 'undefined') { 79 | return destroy(target); 80 | } else if (path) { 81 | const chain = path.split('.'); 82 | const [prop,] = chain; 83 | const set = observers.get(path); 84 | if (set) { 85 | if (callback) { 86 | set.delete(callback); 87 | } else { 88 | set.clear(); 89 | } 90 | const nextInChain = target[prop]; 91 | if (typeof nextInChain === 'object') { 92 | const observeMethods = registeredObservables.get(nextInChain); 93 | if (observeMethods) { 94 | const {unobserve} = observeMethods; 95 | const path = chain.slice(1).join('.'); 96 | if (path && unobserve !== deadObservableExecution) { 97 | unobserve(path, callback); 98 | } 99 | } 100 | } 101 | if (set.size === 0) { 102 | observers.delete(path); 103 | } 104 | } 105 | } 106 | 107 | return unobserve; 108 | }; 109 | 110 | /** 111 | * @param {string} [path] 112 | * @param {Function} [callback] 113 | * @param {boolean} [invoke = false] 114 | */ 115 | const observe = (path, callback, invoke) => { 116 | if (!registeredObservables.has(target)) { 117 | deadObservableExecution(); 118 | } 119 | const chain = path.split('.'); 120 | const [prop,] = chain; 121 | const isNew = !observers.has(prop); 122 | let callbacks = observers.get(path); 123 | if (!callbacks) { 124 | /** 125 | * @type {Set} 126 | */ 127 | callbacks = new Set(); 128 | observers.set(path, callbacks); 129 | } 130 | callbacks.add(callback); 131 | 132 | if (isNew) { 133 | const initialValue = target[prop]; 134 | if (typeof initialValue === 'object') { 135 | const {observe} = convert(initialValue); 136 | Array.from(observers.entries()).forEach(([path, set]) => { 137 | if (path.startsWith(prop + '.')) { 138 | set.forEach(cb => observe(path.split('.').slice(1).join('.'), cb, invoke)); 139 | } 140 | }) 141 | } 142 | values[prop] = initialValue; 143 | typeof target === 'object' && Object.defineProperty(target, prop, { 144 | get() { 145 | return values[prop] 146 | }, 147 | set(v) { 148 | values[prop] = v; 149 | const set = observers.get(prop); 150 | set && set.forEach(cb => cb(v, prop)); 151 | if (typeof v === 'object') { 152 | const {observe} = convert(v); 153 | Array.from(observers.entries()).forEach(([path, set]) => { 154 | if (path.startsWith(prop + '.')) { 155 | set.forEach(cb => observe(path.split('.').slice(1).join('.'), cb, true)); 156 | } 157 | }) 158 | } 159 | }, 160 | configurable: true 161 | }); 162 | } 163 | 164 | if (invoke) { 165 | const set = observers.get(prop); 166 | set && set.forEach(cb => cb(values[prop], prop)); 167 | } 168 | 169 | return observe; 170 | }; 171 | 172 | metadata.observe = observe; 173 | metadata.unobserve = unobserve; 174 | metadata.values = values; 175 | 176 | registeredObservables.set(target, metadata); 177 | return metadata; 178 | } 179 | 180 | /** 181 | * @private 182 | */ 183 | module.exports = { 184 | convert, 185 | destroy 186 | }; -------------------------------------------------------------------------------- /test/unwatch.test.js: -------------------------------------------------------------------------------- 1 | const {watch, unwatch} = require('../src'); 2 | const assert = require('assert'); 3 | 4 | describe('unwatch', () => { 5 | 6 | describe('De-registering', () => { 7 | const target = { 8 | some: { 9 | path: 123 10 | } 11 | }; 12 | 13 | it('Should throw error when observing a de-registered target', done => { 14 | const observe = watch(target)('some.path', () => { 15 | }); 16 | unwatch(target); 17 | try { 18 | observe('some.path', () => { 19 | }); 20 | } catch (err) { 21 | return done(); 22 | } 23 | done(new Error('unobserve was still executed')); 24 | }); 25 | 26 | it('Should NOT throw error when un-observing a non-registered path', done => { 27 | const callback = () => {}; 28 | watch(target, 'some.path', callback); 29 | try { 30 | unwatch(target, 'x.x.x.x', callback); 31 | unwatch(target, 'y.y.y', callback); 32 | unwatch(target, 'z.z'); 33 | } catch (e) { 34 | done(e); 35 | } 36 | done(); 37 | }); 38 | 39 | it('Should NOT throw error when un-observing a de-registered target', done => { 40 | watch(target, 'some.path', () => done(new Error('Should not be invoked'))); 41 | unwatch(target)('bla')('a.b.c.d')()(); 42 | unwatch(target, 'x.y.z'); 43 | unwatch(target); 44 | unwatch(target)('bla')('a.b.c.d')()(); 45 | done(); 46 | }); 47 | 48 | it('Should allow watch after de-registering a target', done => { 49 | let error = new Error('Registered callback was not invoked'); 50 | watch(target, 'some.path', () => done(new Error('Should not be invoked'))); 51 | unwatch(target); 52 | watch(target, 'some.path', (value) => { 53 | assert.strictEqual(value, 1); 54 | error = undefined; 55 | }); 56 | target.some = { 57 | path: 1 58 | }; 59 | done(error); 60 | }); 61 | }); 62 | 63 | it('Should remove all watchers', done => { 64 | /** 65 | * @type {undefined|string} 66 | */ 67 | let fail = undefined; 68 | const target = { 69 | a: { 70 | b: 12345 71 | } 72 | }; 73 | watch(target, 'a.b', () => fail = 'Unwatch did not work'); 74 | unwatch(target, 'a.b'); 75 | target.a.b = 6; 76 | done(fail); 77 | }); 78 | 79 | it('Should chain watch/unwatch functions', done => { 80 | const target = { 81 | a: { 82 | b: 1 83 | }, 84 | c: 2 85 | }; 86 | const callback = () => done(new Error('Callback should not have been invoked')); 87 | watch(target) 88 | ('a.b', callback) 89 | ('c', callback); 90 | 91 | unwatch(target) 92 | ('a.b', callback) 93 | ('c'); 94 | 95 | target.a = { 96 | b: 2 97 | }; 98 | target.c = 5; 99 | done(); 100 | }); 101 | 102 | it('Should not remove deep nested watchers when removing intermediate watcher', done => { 103 | const target = { 104 | level1: { 105 | level2: { 106 | level3: { 107 | value: 12345 108 | } 109 | } 110 | } 111 | }; 112 | let count = 0; 113 | const callback = () => { 114 | done('Should be unwatched'); 115 | }; 116 | const assertionCallback = (value, prop) => { 117 | count++; 118 | if (count === 1) { 119 | assert.strictEqual(value, 5678); 120 | assert.strictEqual(prop, 'value'); 121 | } 122 | if (count === 2) { 123 | assert.strictEqual(prop, 'level2'); 124 | assert.deepStrictEqual(value, { 125 | level3: { 126 | value: 5678 127 | } 128 | }); 129 | done(); 130 | } 131 | }; 132 | watch(target, 'level1.level2.level3.value', assertionCallback); 133 | watch(target, 'level1.level2.level3', callback); 134 | watch(target, 'level1.level2', assertionCallback); 135 | unwatch(target, 'level1.level2.level3', callback); 136 | 137 | target.level1.level2.level3 = { 138 | value: 5678 139 | }; 140 | 141 | target.level1.level2 = { 142 | level3: { 143 | value: 5678 144 | } 145 | }; 146 | }); 147 | 148 | it('Should remove specific callback on a nested object', done => { 149 | const target = { 150 | a: { 151 | b: 12345 152 | } 153 | }; 154 | const callback = () => { 155 | done('Unwatch did not work'); 156 | }; 157 | watch(target, 'a.b', callback); 158 | watch(target, 'a.b', (value, prop) => { 159 | assert.strictEqual(value, 6); 160 | assert.strictEqual(prop, 'b'); 161 | done(); 162 | }); 163 | unwatch(target, 'a.b', callback); 164 | target.a.b = 6; 165 | }); 166 | 167 | }); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 |

oculusx

Easy javascript object observing for browser and node

51 |
52 |

Oculus is a latin word for eye or sight

53 |
54 |

See Demo

55 |

Watching


 56 | import { watch } from 'oculusx';
 57 | 
 58 | const appData = {
 59 |     // empty state at first, but it can be a fully populated object
 60 | };
 61 | 
 62 | watch(appData)
 63 |     ('user.name', (value) => {
 64 |         // do something when the name changes
 65 |     })
 66 |     ('user', (value) => {
 67 |         // do something when the whole user object changes
 68 |         // subscription to name remains even if the user is a whole new object and will be invoked
 69 |     });
 70 | 
 71 | appData.user = {
 72 |     name: 'John Doe' // both wathcers are inboked
 73 | };
 74 | 
 75 | appData.user.name = 'Jane Doe'; // user.name watcher is invoked
 76 | 
 77 | appData.user = undefined; // both watchers are invoked
 78 | 
 79 | appData.user = {
 80 |     name: 'Jordan Doe' // user.name is still intact and both watchers will be invoked.
 81 | };

Unwatching a specific callback

import { watch, unwatch } from 'oculusx'
 82 | 
 83 | const handler_1 = (value) => {
 84 |     // do something
 85 | };
 86 | 
 87 | const handler_2 = (value) => {
 88 |     // do something
 89 | };
 90 | 
 91 | watch(appData)
 92 |     ('user.name', handler_1)
 93 |     ('user.name', handler_2);
 94 | 
 95 | unwatch(appData)('user.name', handler_1);
 96 | 
 97 | appData.user = {
 98 |     name: 'Jaquelin Doe' // only handler_2 is invoked
 99 | };

Unwatching all callbacks from a specific path

import { watch, unwatch } from 'oculusx';
100 | 
101 | watch(appData)
102 |     ('user', () => { /* do something useful */ }) // callback #1
103 |     ('user', () => { /* do something useful */ }) // callback #2
104 |     ('user.name', () => { /* do something useful */ }); // watch user.name
105 | 
106 | unwatch(appData)('user'); // removes all user object watchers
107 | 
108 | appData.user.name = 'James Doe'; // user.name watcher is invoked
109 | appData.user = {
110 |     name: 'Jimmy Doe' // Only user.name watcher is invoked
111 | };
112 | 

Immediate invocation


113 | watch(target)(path, callback, true);
114 | 

Alternative syntax

For those who dislike currying syntax:

115 |
watch(target)
116 |     (path, callback)
117 |     (path2, callback2);
118 | 
119 | // equals
120 | watch(target, path, callback);
121 | watch(target, path2, callback2);
122 | 
123 | // or
124 | watch(target, path, callback)
125 |     (path2, callback2);

It also works the same for unwatch.

126 |

DOM Binding - browser only feature

Demo

127 |

Interface:

type NodeBindingOptions = {
128 |   method?: 'attribute'|'text', // defaults to 'text'
129 |   attribute?: string, // attribute to be changed applicable only when method is 'attribute',
130 |   compute?: Function // observed value can be computed before injected to the element
131 | };
132 | 
133 | bindToElement (model: Object, target: Element, path: string, options?:NodeBindingOptions) => Function
134 | // returns unsubscribe function to stop binding

Example:

import 'oculusx/bind-to-element';
135 | const someElement = document.querySelector('#myElement');
136 | const model = {}
137 | const unsubscribe = oculusx.bindToElement(model, someElement, 'path.to.data');
138 | model.path.to = {
139 |   data: 'Hello, world'
140 | }; // element's text changed to 'Hello, world'
141 | unsubscribe(); // element is no longer bound to model

Using options:

const someElement = document.querySelector('#myElement');
142 | const unsubscribe = oculusx.bindToElement(model, someElement, 'path.to.data', {
143 |   method: 'attribute',
144 |   attribute: 'my-attr-name',
145 |   compute: x => x.toLowerCase()
146 | });

Future releases

    147 |
  • Unwatching all nested pathes: unwatch(target)('some.path.*');
  • 148 |
  • Unwatching all pathes from an object: unwatch(target, '*');
  • 149 |
  • Watching arrays. Currently partially working only when shift/unshift are invoked or the whole array is replaced
  • 150 |
  • Documentation
  • 151 |
152 |

Installation

npm i oculusx 153 | yarn add oculusx

154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 |
162 | 163 |
164 | 165 |
166 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 167 |
168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/convert.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | convert.js - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

convert.js

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
/**
 42 |  * @module oculusx
 43 |  * @ignore
 44 |  * @typedef ObservableMetadata
 45 |  * @property {Function|undefined} observe
 46 |  * @property {Function|undefined} unobserve,
 47 |  * @property {Map<string, Set<Function>>} observers
 48 |  * @property {object} [values = {}]
 49 |  */
 50 | 
 51 | 
 52 | /**
 53 |  * @private
 54 |  * @type {WeakMap<object, ObservableMetadata>}
 55 |  */
 56 | const registeredObservables = new WeakMap();
 57 | 
 58 | /**
 59 |  * @private
 60 |  * @returns {Function}
 61 |  */
 62 | const noOp = () => noOp;
 63 | 
 64 | /**
 65 |  * @private
 66 |  */
 67 | const deadObservableExecution = function () {
 68 |   throw new Error('Cannot watch a non-registered object');
 69 | };
 70 | 
 71 | /**
 72 |  * @private
 73 |  * @param {object} target
 74 |  * @returns {Function}
 75 |  */
 76 | function destroy(target) {
 77 |   const meta = registeredObservables.get(target);
 78 |   registeredObservables.delete(target);
 79 |   if (meta) {
 80 |     meta.observe = deadObservableExecution;
 81 |     meta.observers.clear();
 82 |     meta.values = {};
 83 |     return meta.unobserve;
 84 |   }
 85 |   return noOp;
 86 | }
 87 | 
 88 | /**
 89 |  * Enables object observing on a target object
 90 |  * @private
 91 |  * @param {object} target
 92 |  * @returns {ObservableMetadata}
 93 |  */
 94 | function convert(target) {
 95 |   /**
 96 |    * @type {ObservableMetadata}
 97 |    */
 98 |   let metadata = registeredObservables.get(target);
 99 |   if (metadata) {
100 |     return metadata;
101 |   } else {
102 |     metadata = {
103 |       observers: new Map(),
104 |       observe: undefined,
105 |       unobserve: undefined,
106 |       values: {}
107 |     };
108 |   }
109 | 
110 |   const {observers, values} = metadata;
111 | 
112 |   /**
113 |    * @param [path]
114 |    * @param {Function} [callback]
115 |    * @returns {unobserve}
116 |    */
117 |   const unobserve = (path, callback) => {
118 |     if (typeof path === 'undefined') {
119 |       return destroy(target);
120 |     } else if (path) {
121 |       const chain = path.split('.');
122 |       const [prop,] = chain;
123 |       const set = observers.get(path);
124 |       if (set) {
125 |         if (callback) {
126 |           set.delete(callback);
127 |         } else {
128 |           set.clear();
129 |         }
130 |         const nextInChain = target[prop];
131 |         if (typeof nextInChain === 'object') {
132 |           const observeMethods = registeredObservables.get(nextInChain);
133 |           if (observeMethods) {
134 |             const {unobserve} = observeMethods;
135 |             const path = chain.slice(1).join('.');
136 |             if (path && unobserve !== deadObservableExecution) {
137 |               unobserve(path, callback);
138 |             }
139 |           }
140 |         }
141 |         if (set.size === 0) {
142 |           observers.delete(path);
143 |         }
144 |       }
145 |     }
146 | 
147 |     return unobserve;
148 |   };
149 | 
150 |   /**
151 |    * @param {string} [path]
152 |    * @param {Function} [callback]
153 |    * @param {boolean} [invoke = false]
154 |    */
155 |   const observe = (path, callback, invoke) => {
156 |     if (!registeredObservables.has(target)) {
157 |       deadObservableExecution();
158 |     }
159 |     const chain = path.split('.');
160 |     const [prop,] = chain;
161 |     const isNew = !observers.has(prop);
162 |     let callbacks = observers.get(path);
163 |     if (!callbacks) {
164 |       /**
165 |        * @type {Set<Function>}
166 |        */
167 |       callbacks = new Set();
168 |       observers.set(path, callbacks);
169 |     }
170 |     callbacks.add(callback);
171 | 
172 |     if (isNew) {
173 |       const initialValue = target[prop];
174 |       if (typeof initialValue === 'object') {
175 |         const {observe} = convert(initialValue);
176 |         Array.from(observers.entries()).forEach(([path, set]) => {
177 |           if (path.startsWith(prop + '.')) {
178 |             set.forEach(cb => observe(path.split('.').slice(1).join('.'), cb, invoke));
179 |           }
180 |         })
181 |       }
182 |       values[prop] = initialValue;
183 |       typeof target === 'object' && Object.defineProperty(target, prop, {
184 |         get() {
185 |           return values[prop]
186 |         },
187 |         set(v) {
188 |           values[prop] = v;
189 |           const set = observers.get(prop);
190 |           set && set.forEach(cb => cb(v, prop));
191 |           if (typeof v === 'object') {
192 |             const {observe} = convert(v);
193 |             Array.from(observers.entries()).forEach(([path, set]) => {
194 |               if (path.startsWith(prop + '.')) {
195 |                 set.forEach(cb => observe(path.split('.').slice(1).join('.'), cb, true));
196 |               }
197 |             })
198 |           }
199 |         },
200 |         configurable: true
201 |       });
202 |     }
203 | 
204 |     if (invoke) {
205 |       const set = observers.get(prop);
206 |       set && set.forEach(cb => cb(values[prop], prop));
207 |     }
208 | 
209 |     return observe;
210 |   };
211 | 
212 |   metadata.observe = observe;
213 |   metadata.unobserve = unobserve;
214 |   metadata.values = values;
215 | 
216 |   registeredObservables.set(target, metadata);
217 |   return metadata;
218 | }
219 | 
220 | /**
221 |  * @private
222 |  */
223 | module.exports = {
224 |   convert,
225 |   destroy
226 | };
227 |
228 |
229 | 230 | 231 | 232 | 233 |
234 | 235 |
236 | 237 |
238 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 239 |
240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /docs/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Global - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

Global

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 |

44 | 45 |

46 | 47 | 48 |
49 | 50 |
51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 | 93 | 94 | 95 | 96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |

Type Definitions

114 | 115 | 116 | 117 | 118 | 119 | 120 |

Observe(path, callback, invokeopt)

121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
Parameters:
135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 174 | 175 | 176 | 183 | 184 | 185 | 186 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 209 | 210 | 211 | 218 | 219 | 220 | 221 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 244 | 245 | 246 | 255 | 256 | 257 | 258 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 |
NameTypeAttributesDefaultDescription
path 167 | 168 | 169 | string 170 | 171 | 172 | 173 | 177 | 178 | 179 | 180 | 181 | 182 | 187 | 188 |
callback 202 | 203 | 204 | function 205 | 206 | 207 | 208 | 212 | 213 | 214 | 215 | 216 | 217 | 222 | 223 |
invoke 237 | 238 | 239 | boolean 240 | 241 | 242 | 243 | 247 | 248 | <optional>
249 | 250 | 251 | 252 | 253 | 254 |
259 | 260 | false 261 | 262 |
271 | 272 | 273 | 274 | 275 | 276 | 277 |
278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 |
Source:
305 |
308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 |
316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 |

Unobserve(path, callback)

338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 |
Parameters:
352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 |
NameTypeDescription
path 380 | 381 | 382 | string 383 | 384 | 385 | 386 |
callback 403 | 404 | 405 | function 406 | 407 | 408 | 409 |
421 | 422 | 423 | 424 | 425 | 426 | 427 |
428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 |
Source:
455 |
458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 |
466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 |
487 | 488 |
489 | 490 | 491 | 492 | 493 |
494 | 495 |
496 | 497 |
498 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 499 |
500 | 501 | 502 | 503 | 504 | -------------------------------------------------------------------------------- /docs/oculusx.module_DOM.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DOM - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

DOM

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Source:
81 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 | 93 | 94 | 95 | 96 |
97 | 98 | 99 | 100 | 101 |

Requires

102 | 103 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |

Methods

118 | 119 | 120 | 121 | 122 | 123 | 124 |

(inner) bindToElement(model, node, path, options) → {function}

125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |
Parameters:
139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 |
NameTypeDescription
model 167 | 168 | 169 | object 170 | 171 | 172 | 173 |
node 190 | 191 | 192 | Element 193 | 194 | 195 | 196 |
path 213 | 214 | 215 | string 216 | 217 | 218 | 219 |
options 236 | 237 | 238 | NodeBindingOptions 239 | 240 | 241 | 242 |
254 | 255 | 256 | 257 | 258 | 259 | 260 |
261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 |
Source:
288 |
291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 |
299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 |
Returns:
313 | 314 | 315 |
316 | function to unbind the defined binding 317 |
318 | 319 | 320 | 321 |
322 |
323 | Type 324 |
325 |
326 | 327 | function 328 | 329 | 330 |
331 |
332 | 333 | 334 | 335 | 336 |
Example
337 | 338 |
const element = document.getElementById('#my-element');
339 | const model = {};
340 | const unsubscribe = oculusx.bindToElement(model, element, 'path.to.watch');
341 | model.path = { to: watch: 12345 }; // DOM node changes
342 | unsubscribe();
343 | model.path.to.watch = 'Hello'; // DOM node does not change
344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 |

Type Definitions

352 | 353 | 354 | 355 |

NodeBindingOptions

356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 |
Properties:
367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 410 | 411 | 412 | 419 | 420 | 421 | 422 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 447 | 448 | 449 | 456 | 457 | 458 | 459 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 482 | 483 | 484 | 491 | 492 | 493 | 494 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 |
NameTypeAttributesDefaultDescription
method 400 | 401 | 402 | 'attribute' 403 | | 404 | 405 | 'text' 406 | 407 | 408 | 409 | 413 | 414 | <optional>
415 | 416 | 417 | 418 |
423 | 424 | 'text' 425 | 426 |
attribute 440 | 441 | 442 | string 443 | 444 | 445 | 446 | 450 | 451 | <optional>
452 | 453 | 454 | 455 |
460 | 461 |
compute 475 | 476 | 477 | function 478 | 479 | 480 | 481 | 485 | 486 | <optional>
487 | 488 | 489 | 490 |
495 | 496 |
505 | 506 | 507 | 508 | 509 |
510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 |
Source:
537 |
540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 |
548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 |
559 | 560 |
561 | 562 | 563 | 564 | 565 |
566 | 567 |
568 | 569 |
570 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 571 |
572 | 573 | 574 | 575 | 576 | -------------------------------------------------------------------------------- /docs/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Lato'; 3 | src: url('../fonts/Lato-Regular.ttf') format('truetype'); 4 | } 5 | 6 | @font-face { 7 | font-family: 'Source Code Pro'; 8 | src: url('../fonts/SourceCodePro-Regular.ttf') format('truetype'); 9 | } 10 | 11 | * { 12 | box-sizing: border-box 13 | } 14 | 15 | html, body { 16 | height: 100%; 17 | width: 100%; 18 | } 19 | 20 | body { 21 | color: #4d4e53; 22 | background-color: white; 23 | margin: 0 auto; 24 | padding: 0 0; 25 | 26 | font-family: 'Lato'; 27 | font-size: 16px; 28 | line-height: 160%; 29 | } 30 | 31 | a, 32 | a:active { 33 | color: #000; 34 | text-decoration: none; 35 | } 36 | 37 | a:hover { 38 | text-decoration: underline 39 | } 40 | 41 | p, ul, ol, blockquote { 42 | margin-bottom: 1em; 43 | } 44 | 45 | h1, h2, h3, h4, h5, h6 { 46 | font-family: 'Lato'; 47 | } 48 | 49 | h1, h2, h3, h4, h5, h6 { 50 | color: #000; 51 | font-weight: 400; 52 | margin: 0; 53 | } 54 | 55 | h1 { 56 | font-weight: 300; 57 | font-size: 48px; 58 | margin: 1em 0 .5em; 59 | } 60 | 61 | h1.page-title { 62 | font-size: 48px; 63 | margin: 1em 30px; 64 | } 65 | 66 | h2 { 67 | font-size: 24px; 68 | margin: 1.5em 0 .3em; 69 | } 70 | 71 | h3 { 72 | font-size: 24px; 73 | margin: 1.2em 0 .3em; 74 | } 75 | 76 | h4 { 77 | font-size: 18px; 78 | margin: 1em 0 .2em; 79 | color: #4d4e53; 80 | } 81 | 82 | h5, .container-overview .subsection-title { 83 | font-size: 120%; 84 | letter-spacing: -0.01em; 85 | margin: 8px 0 3px 0; 86 | } 87 | 88 | h6 { 89 | font-size: 100%; 90 | letter-spacing: -0.01em; 91 | margin: 6px 0 3px 0; 92 | font-style: italic; 93 | } 94 | 95 | tt, code, kbd, samp { 96 | font-family: 'Source Code Pro'; 97 | font-size: 80%; 98 | background: #f4f4f4; 99 | padding: 1px 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | .class-description { 104 | font-size: 130%; 105 | line-height: 140%; 106 | margin-bottom: 1em; 107 | margin-top: 1em; 108 | } 109 | 110 | .class-description:empty { 111 | margin: 0 112 | } 113 | 114 | #main { 115 | margin-left: 250px; 116 | min-width: 360px; 117 | width: calc(100% - 250px); 118 | } 119 | 120 | header { 121 | display: block 122 | } 123 | 124 | section { 125 | display: block; 126 | background-color: #fff; 127 | padding: 0 30px; 128 | } 129 | 130 | .variation { 131 | display: none 132 | } 133 | 134 | .signature-attributes { 135 | font-size: 60%; 136 | color: #aaa; 137 | font-style: italic; 138 | font-weight: lighter; 139 | } 140 | 141 | nav { 142 | position: fixed; 143 | top: 0; 144 | display: block; 145 | width: 250px; 146 | height: 100%; 147 | background: rgb(51,51,51); 148 | overflow-y: auto; 149 | } 150 | 151 | nav h2 { 152 | margin: 0; 153 | padding: 0; 154 | background: rgb(18, 102, 150); 155 | } 156 | 157 | nav > h2 > a { 158 | display: block; 159 | width: 100%; 160 | height: 100%; 161 | color: #fff !important; 162 | font-weight: 700; 163 | margin: 0; 164 | padding: 10px 10px 6px 10px; 165 | } 166 | 167 | nav h2, 168 | nav h3 { 169 | margin: 0; 170 | font-size: 13px; 171 | text-transform: uppercase; 172 | letter-spacing: 1px; 173 | font-weight: 700; 174 | line-height: 24px; 175 | color: #fff !important; 176 | } 177 | 178 | nav h3 { 179 | padding: 10px 10px 6px 10px; 180 | } 181 | nav ul { 182 | font-family: 'Lato'; 183 | font-size: 100%; 184 | line-height: 17px; 185 | padding: 0; 186 | margin: 0; 187 | list-style-type: none; 188 | } 189 | 190 | nav ul a { 191 | font-family: 'Lato'; 192 | line-height: 18px; 193 | padding: 0; 194 | display: block; 195 | font-size: 12px; 196 | transition: padding 0.2s, color 0.2s, font-weight 0.2s; 197 | } 198 | 199 | nav ul a:hover { 200 | color: #fff; 201 | text-decoration: none; 202 | font-weight: bold; 203 | } 204 | 205 | nav > ul { 206 | padding: 0; 207 | } 208 | 209 | nav > ul > li > a { 210 | margin: 0; 211 | color: #999; 212 | padding: 4px 10px 4px 10px; 213 | font-weight: 500; 214 | } 215 | 216 | nav ul ul { 217 | margin-bottom: 10px 218 | } 219 | 220 | nav ul ul a { 221 | color: hsl(207, 1%, 60%); 222 | border-left: 1px solid hsl(207, 10%, 86%); 223 | } 224 | 225 | nav ul ul a, 226 | nav ul ul a:active { 227 | padding-left: 20px 228 | } 229 | 230 | footer { 231 | color: hsl(0, 0%, 28%); 232 | margin-left: 250px; 233 | display: block; 234 | padding: 30px; 235 | font-size: 80%; 236 | color: #999; 237 | text-transform: lowercase; 238 | font-style: italic; 239 | } 240 | 241 | .ancestors { 242 | color: #999 243 | } 244 | 245 | .ancestors a { 246 | color: #999 !important; 247 | text-decoration: none; 248 | } 249 | 250 | .clear { 251 | clear: both 252 | } 253 | 254 | .important { 255 | font-weight: bold; 256 | color: #950B02; 257 | } 258 | 259 | .yes-def { 260 | text-indent: -1000px; 261 | } 262 | 263 | .type-signature { 264 | color: #aaa; 265 | } 266 | 267 | .name, .signature { 268 | font-family: 'Source Code Pro'; 269 | font-size: 80%; 270 | } 271 | 272 | .details { 273 | margin: 0; 274 | color: #999; 275 | } 276 | 277 | .details .tag-source a { 278 | color: #999; 279 | } 280 | .details dt { 281 | width: 120px; 282 | float: left; 283 | padding-left: 10px; 284 | } 285 | 286 | .details dd { 287 | margin-left: 70px 288 | } 289 | 290 | .details ul { 291 | margin: 0 292 | } 293 | 294 | .details ul { 295 | list-style-type: none 296 | } 297 | 298 | .details li { 299 | margin-left: 30px 300 | } 301 | 302 | .details pre.prettyprint { 303 | margin: 0 304 | } 305 | 306 | .details .object-value { 307 | padding-top: 0 308 | } 309 | 310 | .description { 311 | margin-bottom: 1em; 312 | margin-top: 1em; 313 | } 314 | 315 | .code-caption { 316 | font-style: italic; 317 | font-size: 107%; 318 | margin: 0; 319 | } 320 | 321 | .prettyprint { 322 | font-size: 13px; 323 | border: 1px solid #ddd; 324 | border-radius: 3px; 325 | box-shadow: 0 1px 3px hsla(0, 0%, 0%, 0.05); 326 | overflow: auto; 327 | } 328 | 329 | .prettyprint.source { 330 | width: inherit 331 | } 332 | 333 | .prettyprint code { 334 | font-size: 100%; 335 | line-height: 18px; 336 | display: block; 337 | margin: 0 30px; 338 | background-color: #fff; 339 | color: #4D4E53; 340 | } 341 | 342 | .prettyprint > code { 343 | padding: 15px 344 | } 345 | 346 | .prettyprint .linenums code { 347 | padding: 0 15px 348 | } 349 | 350 | .prettyprint .linenums li:first-of-type code { 351 | padding-top: 15px 352 | } 353 | 354 | .prettyprint code span.line { 355 | display: inline-block 356 | } 357 | 358 | .prettyprint.linenums { 359 | padding-left: 70px; 360 | -webkit-user-select: none; 361 | -moz-user-select: none; 362 | -ms-user-select: none; 363 | user-select: none; 364 | } 365 | 366 | .prettyprint.linenums ol { 367 | padding-left: 0 368 | } 369 | 370 | .prettyprint.linenums li { 371 | border-left: 3px #ddd solid 372 | } 373 | 374 | .prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { 375 | background-color: lightyellow 376 | } 377 | 378 | .prettyprint.linenums li * { 379 | -webkit-user-select: text; 380 | -moz-user-select: text; 381 | -ms-user-select: text; 382 | user-select: text; 383 | } 384 | 385 | .params, .props { 386 | border-spacing: 0; 387 | border: 1px solid #ddd; 388 | border-collapse: collapse; 389 | border-radius: 3px; 390 | box-shadow: 0 1px 3px rgba(0,0,0,0.1); 391 | width: 100%; 392 | font-size: 14px; 393 | } 394 | 395 | .params .name, .props .name, .name code { 396 | color: #4D4E53; 397 | font-family: 'Source Code Pro'; 398 | font-size: 80%; 399 | } 400 | 401 | .params td, .params th, .props td, .props th { 402 | margin: 0px; 403 | text-align: left; 404 | vertical-align: top; 405 | padding: 10px; 406 | display: table-cell; 407 | } 408 | 409 | .params td { 410 | border-top: 1px solid #eee 411 | } 412 | 413 | .params thead tr, .props thead tr { 414 | background-color: #fff; 415 | font-weight: bold; 416 | } 417 | 418 | .params .params thead tr, .props .props thead tr { 419 | background-color: #fff; 420 | font-weight: bold; 421 | } 422 | 423 | .params td.description > p:first-child, .props td.description > p:first-child { 424 | margin-top: 0; 425 | padding-top: 0; 426 | } 427 | 428 | .params td.description > p:last-child, .props td.description > p:last-child { 429 | margin-bottom: 0; 430 | padding-bottom: 0; 431 | } 432 | 433 | dl.param-type { 434 | border-bottom: 1px solid hsl(0, 0%, 87%); 435 | margin-bottom: 30px; 436 | padding-bottom: 30px; 437 | } 438 | 439 | .param-type dt, .param-type dd { 440 | display: inline-block 441 | } 442 | 443 | .param-type dd { 444 | font-family: 'Source Code Pro'; 445 | font-size: 80%; 446 | } 447 | 448 | .disabled { 449 | color: #454545 450 | } 451 | 452 | /* navicon button */ 453 | .navicon-button { 454 | display: none; 455 | position: relative; 456 | padding: 2.0625rem 1.5rem; 457 | transition: 0.25s; 458 | cursor: pointer; 459 | user-select: none; 460 | opacity: .8; 461 | } 462 | .navicon-button .navicon:before, .navicon-button .navicon:after { 463 | transition: 0.25s; 464 | } 465 | .navicon-button:hover { 466 | transition: 0.5s; 467 | opacity: 1; 468 | } 469 | .navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { 470 | transition: 0.25s; 471 | } 472 | .navicon-button:hover .navicon:before { 473 | top: .825rem; 474 | } 475 | .navicon-button:hover .navicon:after { 476 | top: -.825rem; 477 | } 478 | 479 | /* navicon */ 480 | .navicon { 481 | position: relative; 482 | width: 2.5em; 483 | height: .3125rem; 484 | background: #000; 485 | transition: 0.3s; 486 | border-radius: 2.5rem; 487 | } 488 | .navicon:before, .navicon:after { 489 | display: block; 490 | content: ""; 491 | height: .3125rem; 492 | width: 2.5rem; 493 | background: #000; 494 | position: absolute; 495 | z-index: -1; 496 | transition: 0.3s 0.25s; 497 | border-radius: 1rem; 498 | } 499 | .navicon:before { 500 | top: .625rem; 501 | } 502 | .navicon:after { 503 | top: -.625rem; 504 | } 505 | 506 | /* open */ 507 | .nav-trigger:checked + label:not(.steps) .navicon:before, 508 | .nav-trigger:checked + label:not(.steps) .navicon:after { 509 | top: 0 !important; 510 | } 511 | 512 | .nav-trigger:checked + label .navicon:before, 513 | .nav-trigger:checked + label .navicon:after { 514 | transition: 0.5s; 515 | } 516 | 517 | /* Minus */ 518 | .nav-trigger:checked + label { 519 | transform: scale(0.75); 520 | } 521 | 522 | /* × and + */ 523 | .nav-trigger:checked + label.plus .navicon, 524 | .nav-trigger:checked + label.x .navicon { 525 | background: transparent; 526 | } 527 | 528 | .nav-trigger:checked + label.plus .navicon:before, 529 | .nav-trigger:checked + label.x .navicon:before { 530 | transform: rotate(-45deg); 531 | background: #FFF; 532 | } 533 | 534 | .nav-trigger:checked + label.plus .navicon:after, 535 | .nav-trigger:checked + label.x .navicon:after { 536 | transform: rotate(45deg); 537 | background: #FFF; 538 | } 539 | 540 | .nav-trigger:checked + label.plus { 541 | transform: scale(0.75) rotate(45deg); 542 | } 543 | 544 | .nav-trigger:checked ~ nav { 545 | left: 0 !important; 546 | } 547 | 548 | .nav-trigger:checked ~ .overlay { 549 | display: block; 550 | } 551 | 552 | .nav-trigger { 553 | position: fixed; 554 | top: 0; 555 | clip: rect(0, 0, 0, 0); 556 | } 557 | 558 | .overlay { 559 | display: none; 560 | position: fixed; 561 | top: 0; 562 | bottom: 0; 563 | left: 0; 564 | right: 0; 565 | width: 100%; 566 | height: 100%; 567 | background: hsla(0, 0%, 0%, 0.5); 568 | z-index: 1; 569 | } 570 | 571 | @media only screen and (min-width: 320px) and (max-width: 680px) { 572 | body { 573 | overflow-x: hidden; 574 | } 575 | 576 | nav { 577 | top: 0; 578 | right: 0; 579 | bottom: 0; 580 | left: -250px; 581 | z-index: 3; 582 | transition: left 0.2s; 583 | } 584 | 585 | .navicon-button { 586 | display: inline-block; 587 | position: fixed; 588 | top: 1.5em; 589 | right: 0; 590 | z-index: 2; 591 | } 592 | 593 | #main { 594 | width: 100%; 595 | min-width: 360px; 596 | margin-left: 0; 597 | padding: 0 25px; 598 | } 599 | 600 | #main h1.page-title { 601 | margin: 1em 0; 602 | } 603 | 604 | #main section { 605 | padding: 0; 606 | } 607 | 608 | footer { 609 | margin-left: 0; 610 | } 611 | } 612 | -------------------------------------------------------------------------------- /docs/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 2 | 3 | 4 | 5 | oculusx - Documentation 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |
30 | 31 |

oculusx

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
Source:
99 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 |
129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |

Members

142 | 143 | 144 | 145 |

(static) unwatch

146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |
157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 |
Source:
184 |
187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 |

(static) watch

204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 |
Source:
242 |
245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 |

Methods

264 | 265 | 266 | 267 | 268 | 269 | 270 |

(static) unwatch(target, pathopt, callbackopt) → {Unobserve}

271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 |
Parameters:
285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 322 | 323 | 324 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 353 | 354 | 355 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 386 | 387 | 388 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 |
NameTypeAttributesDescription
target 315 | 316 | 317 | object 318 | 319 | 320 | 321 | 325 | 326 | 327 | 328 | 329 | 330 |
path 346 | 347 | 348 | string 349 | 350 | 351 | 352 | 356 | 357 | <optional>
358 | 359 | 360 | 361 | 362 | 363 |
callback 379 | 380 | 381 | function 382 | 383 | 384 | 385 | 389 | 390 | <optional>
391 | 392 | 393 | 394 | 395 | 396 |
407 | 408 | 409 | 410 | 411 | 412 | 413 |
414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 |
Source:
441 |
444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 |
452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 |
Returns:
466 | 467 | 468 |
469 | scoped unwatch function on the target object 470 |
471 | 472 | 473 | 474 |
475 |
476 | Type 477 |
478 |
479 | 480 | Unobserve 481 | 482 | 483 |
484 |
485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 |

(static) watch(target, pathopt, callbackopt, invokeopt) → {Observe}

496 | 497 | 498 | 499 | 500 | 501 |
502 | Watches for object changes 503 |
504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 |
Parameters:
514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 553 | 554 | 555 | 562 | 563 | 564 | 565 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 588 | 589 | 590 | 599 | 600 | 601 | 602 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 625 | 626 | 627 | 636 | 637 | 638 | 639 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 662 | 663 | 664 | 673 | 674 | 675 | 676 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 |
NameTypeAttributesDefaultDescription
target 546 | 547 | 548 | object 549 | 550 | 551 | 552 | 556 | 557 | 558 | 559 | 560 | 561 | 566 | 567 |
path 581 | 582 | 583 | string 584 | 585 | 586 | 587 | 591 | 592 | <optional>
593 | 594 | 595 | 596 | 597 | 598 |
603 | 604 |
callback 618 | 619 | 620 | function 621 | 622 | 623 | 624 | 628 | 629 | <optional>
630 | 631 | 632 | 633 | 634 | 635 |
640 | 641 |
invoke 655 | 656 | 657 | boolean 658 | 659 | 660 | 661 | 665 | 666 | <optional>
667 | 668 | 669 | 670 | 671 | 672 |
677 | 678 | false 679 | 680 |
689 | 690 | 691 | 692 | 693 | 694 | 695 |
696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 |
Source:
723 |
726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 |
734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 |
Returns:
748 | 749 | 750 |
751 | watch function scoped to target object only 752 |
753 | 754 | 755 | 756 |
757 |
758 | Type 759 |
760 |
761 | 762 | Observe 763 | 764 | 765 |
766 |
767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 |
779 | 780 |
781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 |
789 | 790 |
791 | 792 | 793 | 794 | 795 | 796 |
797 | 798 |
799 |
800 | 801 | 802 |

Usage

<script src="/path/to/oculusx.js"></script>
803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 |
823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 |
Source:
850 |
853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 |
861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 |
880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 |

Members

893 | 894 | 895 | 896 |

(static) unwatch

897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 |
908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 |
Source:
935 |
938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 |
946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 |

(static) watch

955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 |
966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 |
Source:
993 |
996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 |
1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 |

Methods

1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 |

(static) unwatch(target, pathopt, callbackopt) → {Unobserve}

1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 |
Parameters:
1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1073 | 1074 | 1075 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1104 | 1105 | 1106 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1137 | 1138 | 1139 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 |
NameTypeAttributesDescription
target 1066 | 1067 | 1068 | object 1069 | 1070 | 1071 | 1072 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 |
path 1097 | 1098 | 1099 | string 1100 | 1101 | 1102 | 1103 | 1107 | 1108 | <optional>
1109 | 1110 | 1111 | 1112 | 1113 | 1114 |
callback 1130 | 1131 | 1132 | function 1133 | 1134 | 1135 | 1136 | 1140 | 1141 | <optional>
1142 | 1143 | 1144 | 1145 | 1146 | 1147 |
1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 |
1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 |
Source:
1192 |
1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 |
1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 |
Returns:
1217 | 1218 | 1219 |
1220 | scoped unwatch function on the target object 1221 |
1222 | 1223 | 1224 | 1225 |
1226 |
1227 | Type 1228 |
1229 |
1230 | 1231 | Unobserve 1232 | 1233 | 1234 |
1235 |
1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 |

(static) watch(target, pathopt, callbackopt, invokeopt) → {Observe}

1247 | 1248 | 1249 | 1250 | 1251 | 1252 |
1253 | Watches for object changes 1254 |
1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 |
Parameters:
1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1304 | 1305 | 1306 | 1313 | 1314 | 1315 | 1316 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1339 | 1340 | 1341 | 1350 | 1351 | 1352 | 1353 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1376 | 1377 | 1378 | 1387 | 1388 | 1389 | 1390 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1413 | 1414 | 1415 | 1424 | 1425 | 1426 | 1427 | 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 |
NameTypeAttributesDefaultDescription
target 1297 | 1298 | 1299 | object 1300 | 1301 | 1302 | 1303 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1317 | 1318 |
path 1332 | 1333 | 1334 | string 1335 | 1336 | 1337 | 1338 | 1342 | 1343 | <optional>
1344 | 1345 | 1346 | 1347 | 1348 | 1349 |
1354 | 1355 |
callback 1369 | 1370 | 1371 | function 1372 | 1373 | 1374 | 1375 | 1379 | 1380 | <optional>
1381 | 1382 | 1383 | 1384 | 1385 | 1386 |
1391 | 1392 |
invoke 1406 | 1407 | 1408 | boolean 1409 | 1410 | 1411 | 1412 | 1416 | 1417 | <optional>
1418 | 1419 | 1420 | 1421 | 1422 | 1423 |
1428 | 1429 | false 1430 | 1431 |
1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 |
1447 | 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 |
Source:
1474 |
1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 |
1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 |
Returns:
1499 | 1500 | 1501 |
1502 | watch function scoped to target object only 1503 |
1504 | 1505 | 1506 | 1507 |
1508 |
1509 | Type 1510 |
1511 |
1512 | 1513 | Observe 1514 | 1515 | 1516 |
1517 |
1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | 1526 | 1527 | 1528 | 1529 |
1530 | 1531 |
1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 |
1540 | 1541 |
1542 | 1543 | 1544 | 1545 | 1546 | 1547 |
1548 | 1549 |
1550 |
1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 |
1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | 1597 | 1598 |
Source:
1599 |
1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 |
1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 |
1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 |

Members

1642 | 1643 | 1644 | 1645 |

(static) unwatch

1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 1654 | 1655 | 1656 |
1657 | 1658 | 1659 | 1660 | 1661 | 1662 | 1663 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | 1674 | 1675 | 1676 | 1677 | 1678 | 1679 | 1680 | 1681 | 1682 | 1683 |
Source:
1684 |
1687 | 1688 | 1689 | 1690 | 1691 | 1692 | 1693 | 1694 |
1695 | 1696 | 1697 | 1698 | 1699 | 1700 | 1701 | 1702 | 1703 |

(static) watch

1704 | 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 |
1715 | 1716 | 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | 1728 | 1729 | 1730 | 1731 | 1732 | 1733 | 1734 | 1735 | 1736 | 1737 | 1738 | 1739 | 1740 | 1741 |
Source:
1742 |
1745 | 1746 | 1747 | 1748 | 1749 | 1750 | 1751 | 1752 |
1753 | 1754 | 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | 1761 | 1762 | 1763 |

Methods

1764 | 1765 | 1766 | 1767 | 1768 | 1769 | 1770 |

(static) unwatch(target, pathopt, callbackopt) → {Unobserve}

1771 | 1772 | 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | 1779 | 1780 | 1781 | 1782 | 1783 | 1784 |
Parameters:
1785 | 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | 1795 | 1796 | 1797 | 1798 | 1799 | 1800 | 1801 | 1802 | 1803 | 1804 | 1805 | 1806 | 1807 | 1808 | 1809 | 1810 | 1811 | 1812 | 1813 | 1814 | 1822 | 1823 | 1824 | 1831 | 1832 | 1833 | 1834 | 1835 | 1836 | 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1853 | 1854 | 1855 | 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | 1870 | 1871 | 1872 | 1873 | 1874 | 1875 | 1876 | 1877 | 1878 | 1886 | 1887 | 1888 | 1897 | 1898 | 1899 | 1900 | 1901 | 1902 | 1903 | 1904 | 1905 | 1906 |
NameTypeAttributesDescription
target 1815 | 1816 | 1817 | object 1818 | 1819 | 1820 | 1821 | 1825 | 1826 | 1827 | 1828 | 1829 | 1830 |
path 1846 | 1847 | 1848 | string 1849 | 1850 | 1851 | 1852 | 1856 | 1857 | <optional>
1858 | 1859 | 1860 | 1861 | 1862 | 1863 |
callback 1879 | 1880 | 1881 | function 1882 | 1883 | 1884 | 1885 | 1889 | 1890 | <optional>
1891 | 1892 | 1893 | 1894 | 1895 | 1896 |
1907 | 1908 | 1909 | 1910 | 1911 | 1912 | 1913 |
1914 | 1915 | 1916 | 1917 | 1918 | 1919 | 1920 | 1921 | 1922 | 1923 | 1924 | 1925 | 1926 | 1927 | 1928 | 1929 | 1930 | 1931 | 1932 | 1933 | 1934 | 1935 | 1936 | 1937 | 1938 | 1939 | 1940 |
Source:
1941 |
1944 | 1945 | 1946 | 1947 | 1948 | 1949 | 1950 | 1951 |
1952 | 1953 | 1954 | 1955 | 1956 | 1957 | 1958 | 1959 | 1960 | 1961 | 1962 | 1963 | 1964 | 1965 |
Returns:
1966 | 1967 | 1968 |
1969 | scoped unwatch function on the target object 1970 |
1971 | 1972 | 1973 | 1974 |
1975 |
1976 | Type 1977 |
1978 |
1979 | 1980 | Unobserve 1981 | 1982 | 1983 |
1984 |
1985 | 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 |

(static) watch(target, pathopt, callbackopt, invokeopt) → {Observe}

1996 | 1997 | 1998 | 1999 | 2000 | 2001 |
2002 | Watches for object changes 2003 |
2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 |
Parameters:
2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 | 2030 | 2031 | 2032 | 2033 | 2034 | 2035 | 2036 | 2037 | 2038 | 2039 | 2040 | 2041 | 2042 | 2043 | 2044 | 2045 | 2053 | 2054 | 2055 | 2062 | 2063 | 2064 | 2065 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | 2077 | 2078 | 2079 | 2080 | 2088 | 2089 | 2090 | 2099 | 2100 | 2101 | 2102 | 2105 | 2106 | 2107 | 2108 | 2109 | 2110 | 2111 | 2112 | 2113 | 2114 | 2115 | 2116 | 2117 | 2125 | 2126 | 2127 | 2136 | 2137 | 2138 | 2139 | 2142 | 2143 | 2144 | 2145 | 2146 | 2147 | 2148 | 2149 | 2150 | 2151 | 2152 | 2153 | 2154 | 2162 | 2163 | 2164 | 2173 | 2174 | 2175 | 2176 | 2181 | 2182 | 2183 | 2184 | 2185 | 2186 | 2187 | 2188 |
NameTypeAttributesDefaultDescription
target 2046 | 2047 | 2048 | object 2049 | 2050 | 2051 | 2052 | 2056 | 2057 | 2058 | 2059 | 2060 | 2061 | 2066 | 2067 |
path 2081 | 2082 | 2083 | string 2084 | 2085 | 2086 | 2087 | 2091 | 2092 | <optional>
2093 | 2094 | 2095 | 2096 | 2097 | 2098 |
2103 | 2104 |
callback 2118 | 2119 | 2120 | function 2121 | 2122 | 2123 | 2124 | 2128 | 2129 | <optional>
2130 | 2131 | 2132 | 2133 | 2134 | 2135 |
2140 | 2141 |
invoke 2155 | 2156 | 2157 | boolean 2158 | 2159 | 2160 | 2161 | 2165 | 2166 | <optional>
2167 | 2168 | 2169 | 2170 | 2171 | 2172 |
2177 | 2178 | false 2179 | 2180 |
2189 | 2190 | 2191 | 2192 | 2193 | 2194 | 2195 |
2196 | 2197 | 2198 | 2199 | 2200 | 2201 | 2202 | 2203 | 2204 | 2205 | 2206 | 2207 | 2208 | 2209 | 2210 | 2211 | 2212 | 2213 | 2214 | 2215 | 2216 | 2217 | 2218 | 2219 | 2220 | 2221 | 2222 |
Source:
2223 |
2226 | 2227 | 2228 | 2229 | 2230 | 2231 | 2232 | 2233 |
2234 | 2235 | 2236 | 2237 | 2238 | 2239 | 2240 | 2241 | 2242 | 2243 | 2244 | 2245 | 2246 | 2247 |
Returns:
2248 | 2249 | 2250 |
2251 | watch function scoped to target object only 2252 |
2253 | 2254 | 2255 | 2256 |
2257 |
2258 | Type 2259 |
2260 |
2261 | 2262 | Observe 2263 | 2264 | 2265 |
2266 |
2267 | 2268 | 2269 | 2270 | 2271 | 2272 | 2273 | 2274 | 2275 | 2276 | 2277 | 2278 |
2279 | 2280 |
2281 | 2282 | 2283 | 2284 | 2285 |
2286 | 2287 |
2288 | 2289 |
2290 | Documentation generated at Thu Mar 07 2019 20:50:54 GMT+0200 (Israel Standard Time) 2291 |
2292 | 2293 | 2294 | 2295 | 2296 | --------------------------------------------------------------------------------