├── .gitignore ├── resources ├── api.png ├── logo.png ├── ontology.png └── visualParadigm │ ├── api.vpp │ └── ontology.vpp ├── src ├── umd │ ├── umd-library-epilog.js │ └── umd-library-prolog.js └── core │ ├── dsl │ ├── wrapper.js │ └── parser.js │ ├── api │ └── wrapper.js │ ├── adapter │ ├── wrapper.js │ ├── class.js │ ├── text.js │ ├── attr.js │ ├── value.js │ └── on.js │ ├── repository │ ├── wrapper.js │ ├── connector.js │ └── adapter.js │ ├── connector │ ├── wrapper.js │ ├── debug.js │ ├── split.js │ ├── map.js │ ├── count.js │ ├── filter.js │ ├── ifAll.js │ ├── ifAny.js │ ├── ifNone.js │ ├── join.js │ └── sort.js │ ├── engine │ ├── wrapper.js │ ├── sockets.js │ ├── bindingscope.js │ └── reference.js │ ├── preprocessor │ ├── wrapper.js │ └── preprocessor.js │ ├── util │ ├── wrapper.js │ ├── number.js │ ├── counter.js │ ├── jQuery.js │ ├── map.js │ ├── object.js │ └── common.js │ └── wrapper.js ├── examples ├── TodoMVC │ └── common │ │ └── bg.png ├── Clock │ ├── index.css │ └── index.html └── Calculator │ ├── index.css │ └── index.html ├── VERSION ├── grunt ├── grunt-4-doc.js ├── grunt-7-cty.js ├── grunt-1-def.js ├── grunt-5-tst.js ├── grunt-9-dev.js ├── lint-1-jshint.json ├── grunt-2-env.js ├── grunt-6-cov.js ├── grunt-8-rel.js └── lint-2-eslint.json ├── test ├── selenium │ ├── readme.md │ ├── setEnv.testcase │ ├── 8-when.testcase │ ├── 4-identification.testcase │ ├── 6-deactivate.testcase │ ├── example-clock.testcase │ ├── 9-abort.testcase │ ├── 8-when.html │ ├── 4-identification.html │ ├── 10-initiator.html │ ├── example-calculator.testcase │ ├── 12-parameter.testcase │ ├── 9-abort.html │ ├── 5-cycle-detection.html │ ├── bindingjs.seleniumsuite │ ├── 15-iterationUpdate.html │ ├── 13-expression.testcase │ ├── 16-lambda.testcase │ ├── 3-sockets.html │ ├── 15-iterationUpdate.testcase │ ├── 11-sequence.testcase │ ├── 10-initiator.testcase │ ├── 1-binding.testcase │ ├── 2-iteration.html │ ├── 12-parameter.html │ ├── 6-deactivate.html │ ├── 14-unmount.html │ ├── 16-lambda.html │ ├── 11-sequence.html │ ├── 2-iteration-4.testcase │ ├── 3-sockets.testcase │ ├── 14-unmount.testcase │ ├── 7-pause.testcase │ ├── 1-binding.html │ ├── 2-iteration-3.testcase │ ├── 13-expression.html │ ├── example-TodoMVC.testcase │ └── 2-iteration-2.testcase ├── core │ ├── dsl │ │ ├── parser.bind │ │ └── parser.js │ └── api │ │ ├── viewDataBinding.js │ │ └── api.js └── common.js ├── Gruntfile.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | cov 3 | node_modules 4 | /npm-debug.log 5 | -------------------------------------------------------------------------------- /resources/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/resources/api.png -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/resources/logo.png -------------------------------------------------------------------------------- /resources/ontology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/resources/ontology.png -------------------------------------------------------------------------------- /src/umd/umd-library-epilog.js: -------------------------------------------------------------------------------- 1 | 2 | /* export external API */ 3 | return $api; 4 | })); 5 | 6 | -------------------------------------------------------------------------------- /examples/TodoMVC/common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/examples/TodoMVC/common/bg.png -------------------------------------------------------------------------------- /resources/visualParadigm/api.vpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/resources/visualParadigm/api.vpp -------------------------------------------------------------------------------- /resources/visualParadigm/ontology.vpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rummelj/bindingjs/HEAD/resources/visualParadigm/ontology.vpp -------------------------------------------------------------------------------- /src/core/dsl/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.dsl = {} 11 | 12 | include("parser.js") -------------------------------------------------------------------------------- /src/core/api/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | include("api.js") 11 | include("viewDataBinding.js") -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | ## 2 | ## BindingJS -- View Data Binding for JavaScript 3 | ## Copyright (c) 2014 Ralf S. Engelschall 4 | ## 5 | ## This Source Code Form is subject to the terms of the Mozilla Public 6 | ## License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ## with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | ## 9 | 10 | major: 0 11 | minor: 9 12 | micro: 1 13 | date: 20141011 14 | 15 | -------------------------------------------------------------------------------- /src/core/adapter/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | include("attr.js") 11 | include("class.js") 12 | include("on.js") 13 | include("text.js") 14 | include("value.js") -------------------------------------------------------------------------------- /grunt/grunt-4-doc.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (/* grunt */) { 12 | /* FIXME */ 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/core/repository/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.repository = {} 11 | _api.repository.adapter = {} 12 | _api.repository.connector = {} 13 | 14 | include("adapter.js") 15 | include("connector.js") -------------------------------------------------------------------------------- /src/core/connector/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | include("debug.js") 11 | include("join.js") 12 | include("split.js") 13 | include("filter.js") 14 | include("map.js") 15 | include("sort.js") 16 | include("count.js") 17 | include("ifAny.js") 18 | include("ifAll.js") 19 | include("ifNone.js") -------------------------------------------------------------------------------- /src/core/engine/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.engine = {} 11 | _api.engine.iterator = {} 12 | _api.engine.binding = {} 13 | _api.engine.sockets = {} 14 | 15 | include("binding.js") 16 | include("iterator.js") 17 | include("sockets.js") 18 | include("bindingscope.js") 19 | include("reference.js") -------------------------------------------------------------------------------- /test/selenium/readme.md: -------------------------------------------------------------------------------- 1 | Test Instructions 2 | ----------------- 3 | 4 | - Get Selenium IDE for Firefox (http://docs.seleniumhq.org/download/) 5 | - Open about:config in firefox and change security.fileuri.strict_origin_policy to false 6 | to allow Selenium IDE to open local URLs 7 | - Load the test suite bindingjs.seleniumsuite 8 | - Open the first test case setEnv and modify the target of baseUrl to point 9 | to your local repository of BindingJS 10 | - Open a new tab 11 | - Set the execution speed of the test cases to ~90% (This is necessary since Firefox does not yet support Object.observe and uses the fallback polling of the Model) 12 | - Press "Play entire test suite" in Selenium IDE 13 | -------------------------------------------------------------------------------- /src/core/preprocessor/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.preprocessor = {} 11 | _api.preprocessor.iterator = {} 12 | _api.preprocessor.transform = {} 13 | _api.preprocessor.validate = {} 14 | _api.preprocessor.convenience = {} 15 | 16 | include("iterator.js") 17 | include("preprocessor.js") 18 | include("transform.js") 19 | include("validate.js") 20 | include("convenience.js") -------------------------------------------------------------------------------- /src/core/util/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.util = {} 11 | _api.util.array = {} 12 | _api.util.object = {} 13 | _api.util.dom = {} 14 | _api.util.tree = {} 15 | _api.util.number = {} 16 | _api.util.jQuery = {} 17 | 18 | include("array.js") 19 | include("object.js") 20 | include("common.js") 21 | include("counter.js") 22 | include("tree.js") 23 | include("number.js") 24 | include("map.js") 25 | include("jQuery.js") -------------------------------------------------------------------------------- /src/core/util/number.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.util.number.maxValue = () => { 11 | return Number.MAX_VALUE 12 | } 13 | 14 | _api.util.number.minValue = () => { 15 | return Number.MIN_VALUE 16 | } 17 | 18 | _api.util.number.isWholePositiveNumber = (obj) => { 19 | return _api.util.number.isWholeNumber(obj) && parseInt(obj, 10) >= 0 20 | } 21 | 22 | _api.util.number.isWholeNumber = (obj) => { 23 | return obj % 1 === 0 24 | } -------------------------------------------------------------------------------- /test/selenium/setEnv.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | setEnv 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
setEnv
storefile:///C:/Users/rummelj/bindingjs_mybaseUrl
20 | 21 | 22 | -------------------------------------------------------------------------------- /src/core/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | include("../umd/umd-library-prolog.js", { 11 | library : "bindingjs" 12 | }) 13 | 14 | _api.binding = {} 15 | 16 | include("util/wrapper.js") 17 | include("api/wrapper.js") 18 | include("repository/wrapper.js") 19 | include("dsl/wrapper.js") 20 | include("engine/wrapper.js") 21 | include("preprocessor/wrapper.js") 22 | include("adapter/wrapper.js") 23 | include("connector/wrapper.js") 24 | 25 | include("../umd/umd-library-epilog.js") -------------------------------------------------------------------------------- /src/core/connector/debug.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("debug", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | let msg = _api.util.object.isDefined(params) && params.length > 0 ? 15 | JSON.stringify(_api.util.convertToValues(params[0])) : "No message provided" 16 | msg += ", Input: " + JSON.stringify(_api.util.convertToValues(input)) 17 | $api.debug(1, "Debug Connector (" + msg + ")") 18 | return input 19 | } 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /grunt/grunt-7-cty.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* code complexity reporting */ 13 | grunt.config.merge({ 14 | complexity: { 15 | "core": { 16 | src: [ "src/core/*.js" ], 17 | options: { 18 | errorsOnly: false, 19 | cyclomatic: 40, 20 | halstead: 80, 21 | maintainability: 65 22 | } 23 | } 24 | } 25 | }) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /grunt/grunt-1-def.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* task defaults */ 13 | grunt.config.merge({ 14 | jshint: { 15 | options: { 16 | jshintrc: "grunt/lint-1-jshint.json" 17 | } 18 | }, 19 | eslint: { 20 | options: { 21 | config: "grunt/lint-2-eslint.json" 22 | } 23 | }, 24 | shell: { 25 | options: { 26 | stdout: true, 27 | stderr: true 28 | } 29 | } 30 | }) 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/core/adapter/class.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | $api.plugin("class", () => { 11 | return { 12 | getPaths: (element, path) => { 13 | return [path] 14 | }, 15 | 16 | getValue: (element, path) => { 17 | return element.hasClass(path[0]) 18 | }, 19 | 20 | set: (element, path, value) => { 21 | if (value) { 22 | element.addClass(path[0]) 23 | } else { 24 | element.removeClass(path[0]) 25 | } 26 | }, 27 | 28 | type: () => { 29 | return "view" 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /examples/Clock/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Source Sans Pro'; 3 | color: #555; 4 | line-height: 1.5; 5 | } 6 | 7 | .clock { 8 | width: 30%; 9 | float: left; 10 | margin: 0 1em 0 0; 11 | } 12 | 13 | .left p { 14 | font-size: 2em; 15 | margin: 1em 0 0 0; 16 | } 17 | 18 | .time { 19 | font-family: 'Voltaire'; 20 | font-size: 4em; 21 | margin: 0; 22 | } 23 | 24 | 25 | .square { 26 | position: relative; 27 | width: 100%; 28 | height: 0; 29 | padding-bottom: 100%; 30 | } 31 | 32 | svg { 33 | position: absolute; 34 | width: 100%; 35 | height: 100%; 36 | } 37 | 38 | .clock-face { 39 | stroke: #333; 40 | fill: white; 41 | } 42 | 43 | .minor { 44 | stroke: #999; 45 | stroke-width: 0.5; 46 | } 47 | 48 | .major { 49 | stroke: #333; 50 | stroke-width: 1; 51 | } 52 | 53 | .hour { 54 | stroke: #333; 55 | } 56 | 57 | .minute { 58 | stroke: #666; 59 | } 60 | 61 | .second, .second-counterweight { 62 | stroke: rgb(180,0,0); 63 | } 64 | 65 | .second-counterweight { 66 | stroke-width: 3; 67 | } -------------------------------------------------------------------------------- /src/core/util/counter.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* 11 | ** Generates increasing numbers starting at 0 12 | */ 13 | class Counter { 14 | constructor () { 15 | this.value = 0 16 | } 17 | 18 | /* 19 | ** Gets current value of Counter without increasing it 20 | */ 21 | get () { 22 | return this.value 23 | } 24 | 25 | /* 26 | ** Gets the next value and increases the internal counter 27 | */ 28 | getNext () { 29 | return ++this.value 30 | } 31 | 32 | /* 33 | ** Sets the internal Counter to value 34 | */ 35 | set (value) { 36 | this.value = value 37 | } 38 | } 39 | 40 | _api.util.Counter = Counter 41 | -------------------------------------------------------------------------------- /grunt/grunt-5-tst.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* unit testing */ 13 | grunt.config.merge({ 14 | mochaTest: { 15 | "core": { 16 | src: [ "build/stage2/test/core/**/*.js" ] 17 | }, 18 | options: { 19 | reporter: "spec", 20 | require: "build/stage2/test/common.js", 21 | clearRequireCache: true, 22 | timeout: 86400000 23 | } 24 | } 25 | }) 26 | 27 | /* register testing task */ 28 | grunt.registerTask("test", [ 29 | "mochaTest:core" 30 | ]) 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/core/connector/split.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("split", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1) { 15 | throw _api.util.exception("The split Connector expects exactly " + 16 | "one parameter, which has to be a string. Example: " + 17 | "@firstName, @lastName <- split(\" \") <- $fullName") 18 | } 19 | 20 | let separator = _api.util.convertIfReference(params[0]) 21 | input = input instanceof Array ? input[0] : input 22 | return _api.util.convertIfReference(input).split(separator) 23 | } 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /test/selenium/8-when.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8-when 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
8-when
open${baseUrl}/test/selenium/8-when.html
assertTextcss=span.2bOrNot2ba
assertText//div[@id='template']/div[3]/spanc
assertCssCountspan2
35 | 36 | 37 | -------------------------------------------------------------------------------- /src/core/adapter/text.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global JSON */ 11 | 12 | $api.plugin("text", () => { 13 | return { 14 | getPaths: (element, path) => { 15 | return [path] 16 | }, 17 | 18 | getValue: (element) => { 19 | return element.text() 20 | }, 21 | 22 | set: (element, path, value) => { 23 | if (typeof value === "object") { 24 | try { 25 | value = JSON.stringify(value) 26 | } catch (e) { 27 | value = "{circular object}" 28 | } 29 | } 30 | element.text(value) 31 | }, 32 | 33 | type: () => { 34 | return "view" 35 | } 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /src/core/repository/connector.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.repository.connector.init = () => { 11 | if (!_api.connectorRepo) { 12 | _api.connectorRepo = {} 13 | } 14 | } 15 | 16 | _api.repository.connector.register = (name, impl) => { 17 | _api.repository.connector.init() 18 | _api.connectorRepo[name] = impl 19 | } 20 | 21 | _api.repository.connector.has = (name) => { 22 | _api.repository.adapter.init() 23 | return _api.connectorRepo[name] ? true : false 24 | } 25 | 26 | _api.repository.connector.get = (name) => { 27 | _api.repository.connector.init() 28 | if (!_api.connectorRepo[name]) { 29 | throw _api.util.exception("No connector with name " + name + 30 | " registered") 31 | } 32 | return _api.connectorRepo[name] 33 | } -------------------------------------------------------------------------------- /test/selenium/4-identification.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 4-identification 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
4-identification
open${baseUrl}/test/selenium/4-identification.html
assertTextcss=div.iterate0
assertText//div[@id='template']/div/div[2]1
assertText//div[@id='template']/div/div[3]2
35 | 36 | 37 | -------------------------------------------------------------------------------- /src/core/connector/map.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("map", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The map Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@mapped <- map(elem, index => elem * index) <- $numbers") 18 | } 19 | 20 | let mapFn = params[0] 21 | let result = [] 22 | _api.util.each(input, (element, index) => { 23 | result[index] = mapFn(_api.util.convertIfReference(element), index) 24 | }) 25 | return result 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /src/core/connector/count.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("count", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The count Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@noCompleted <- count(todo => todo.completed) <- $todos") 18 | } 19 | 20 | let countFn = params[0] 21 | let result = 0 22 | _api.util.each(input, (element, index) => { 23 | result += countFn(_api.util.convertIfReference(element), index) ? 1 : 0 24 | }) 25 | return result 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | /* global require: true */ 12 | 13 | module.exports = function (grunt) { 14 | /* initial task configuration */ 15 | grunt.initConfig({ 16 | version: grunt.file.readYAML("VERSION"), 17 | version_string: "<%= version.major %>.<%= version.minor %>.<%= version.micro %>" 18 | }); 19 | 20 | /* common task aliasing */ 21 | grunt.registerTask("default", [ "build" ]); 22 | grunt.registerTask("build", [ "grunt-build", "src-build" ]); 23 | grunt.registerTask("cleanup", [ "src-clean", "grunt-clean" ]); 24 | 25 | /* load foreign tasks */ 26 | require("load-grunt-tasks")(grunt, { pattern: "grunt-*" }); 27 | 28 | /* load own tasks */ 29 | grunt.loadTasks("grunt"); 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /test/selenium/6-deactivate.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 6-deactivate 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
6-deactivate
open${baseUrl}/test/selenium/6-deactivate.html
assertCssCountdiv.first1
assertCssCountdiv.keyFirst1
assertCssCountdiv.second0
assertCssCountdiv.keySecond0
40 | 41 | 42 | -------------------------------------------------------------------------------- /test/selenium/example-clock.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | example-clock 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
example-clock
open${baseUrl}/examples/Clock/index.html
assertNotTextcss=span.hours
assertNotTextcss=span.minutes
assertNotTextcss=span.seconds
assertNotTextcss=p
41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/Calculator/index.css: -------------------------------------------------------------------------------- 1 | #calculator { 2 | width: 300px; 3 | height: 180px; 4 | position: absolute; 5 | left: 50%; 6 | top: 50%; 7 | margin: -100px 0 0 -150px; 8 | border: 5px solid gray; 9 | border-radius: 25px; 10 | background-color: lightblue; 11 | padding-top: 20px; 12 | } 13 | 14 | input { 15 | text-align: center; 16 | font-weight: bold; 17 | font-size: 1em; 18 | font-family: sans-serif; 19 | margin-left: auto; 20 | margin-right: auto; 21 | display: block; 22 | border: 5px solid white; 23 | -webkit-box-shadow: 24 | inset 0 0 8px rgba(0,0,0,0.1), 25 | 0 0 16px rgba(0,0,0,0.1); 26 | -moz-box-shadow: 27 | inset 0 0 8px rgba(0,0,0,0.1), 28 | 0 0 16px rgba(0,0,0,0.1); 29 | box-shadow: 30 | inset 0 0 8px rgba(0,0,0,0.1), 31 | 0 0 16px rgba(0,0,0,0.1); 32 | padding: 15px; 33 | background: rgba(255,255,255,0.5); 34 | } 35 | 36 | span { 37 | display: block; 38 | text-align: center; 39 | font-size: 1.4em; 40 | font-family: sans-serif; 41 | font-weight: bold; 42 | margin-left: auto; 43 | margin-right: auto; 44 | margin-top: 15px; 45 | } -------------------------------------------------------------------------------- /grunt/grunt-9-dev.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* development tasks configuration */ 13 | grunt.config.merge({ 14 | watch: { 15 | "src-core": { 16 | files: [ "src/**/*.js", "src/**/*.pegjs" ], 17 | tasks: [ "stage1", "stage2", "stage3", "test" ] 18 | }, 19 | "test-core": { 20 | files: [ "test/**/*.js" ], 21 | tasks: [ "test" ] 22 | }, 23 | options: { 24 | nospawn: true 25 | } 26 | } 27 | }) 28 | 29 | /* register tasks */ 30 | grunt.registerTask("dev", [ 31 | "stage1", 32 | "stage2", 33 | "stage3", 34 | "test", 35 | "watch" 36 | ]) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/core/connector/filter.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("filter", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The filter Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@filtered <- filter(elem, index => elem % 2 === 0 && index < 5) <- $numbers") 18 | } 19 | 20 | let filterFn = params[0] 21 | let result = [] 22 | _api.util.each(input, (element, index) => { 23 | if (filterFn(_api.util.convertIfReference(element), index)) { 24 | result.push(element) 25 | } 26 | }) 27 | return result 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/core/connector/ifAll.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("ifAll", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The ifAll Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@result <- ifAll(elem => elem > 3) <- $numbers") 18 | } 19 | 20 | let ifFn = params[0] 21 | let result = true 22 | _api.util.each(input, (element, index, _, breaK) => { 23 | if (!ifFn(_api.util.convertIfReference(element), index)) { 24 | result = false 25 | return breaK 26 | } 27 | }) 28 | return result 29 | } 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/core/connector/ifAny.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("ifAny", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The ifAny Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@result <- ifAny(elem => elem > 3) <- $numbers") 18 | } 19 | 20 | let ifFn = params[0] 21 | let result = false 22 | _api.util.each(input, (element, index, _, breaK) => { 23 | if (ifFn(_api.util.convertIfReference(element), index)) { 24 | result = true 25 | return breaK 26 | } 27 | }) 28 | return result 29 | } 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/core/connector/ifNone.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("ifNone", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1 || typeof params[0] !== "function") { 15 | throw _api.util.exception("The ifNone Connector expects exactly " + 16 | "one parameter, which is a lambda expression. Example: " + 17 | "@result <- ifAny(elem => elem > 3) <- $numbers") 18 | } 19 | 20 | let ifFn = params[0] 21 | let result = true 22 | _api.util.each(input, (element, index, _, breaK) => { 23 | if (ifFn(_api.util.convertIfReference(element), index)) { 24 | result = false 25 | return breaK 26 | } 27 | }) 28 | return result 29 | } 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /test/selenium/9-abort.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9-abort 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
9-abort
open${baseUrl}/test/selenium/9-abort.html
assertTextcss=spanbar
getEvalwindow.model.flag = true
assertTextcss=spanfoo
getEvalwindow.model.flag = false
assertTextcss=spanbar
46 | 47 | 48 | -------------------------------------------------------------------------------- /src/core/connector/join.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("join", ($api, _api) => { 12 | return { 13 | process: (input, params) => { 14 | if (params.length !== 1) { 15 | throw _api.util.exception("The join Connector expects exactly " + 16 | "one parameter, which has to be a string. Example: " + 17 | "@fullName <- join(\" \") <- $firstName, $lastName") 18 | } 19 | 20 | input = input instanceof Array ? input : [input] 21 | let result = "" 22 | let separator = _api.util.convertIfReference(params[0]) 23 | _api.util.each(input, (element, index) => { 24 | result += _api.util.convertIfReference(element) 25 | if (index < input.length - 1) { 26 | result += separator 27 | } 28 | }) 29 | return result 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /test/selenium/8-when.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 | -------------------------------------------------------------------------------- /src/core/repository/adapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.repository.adapter.init = () => { 11 | if (!_api.adapterRepo) { 12 | _api.adapterRepo = {} 13 | } 14 | } 15 | 16 | _api.repository.adapter.register = (name, impl) => { 17 | _api.repository.adapter.init() 18 | _api.adapterRepo[name] = impl 19 | } 20 | 21 | _api.repository.adapter.has = (name) => { 22 | _api.repository.adapter.init() 23 | return _api.adapterRepo[name] ? true : false 24 | } 25 | 26 | _api.repository.adapter.get = (name) => { 27 | _api.repository.adapter.init() 28 | if (!_api.adapterRepo[name]) { 29 | throw _api.util.exception("No adapter with name " + name + 30 | " registered") 31 | } 32 | return _api.adapterRepo[name] 33 | } 34 | 35 | _api.repository.adapter.getAll = (type) => { 36 | _api.repository.adapter.init() 37 | let result = [] 38 | for (let name in _api.adapterRepo) { 39 | let impl = _api.repository.adapter.get(name) 40 | if (impl.type() === type) { 41 | result.push(impl) 42 | } 43 | } 44 | return result 45 | } -------------------------------------------------------------------------------- /grunt/lint-1-jshint.json: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr": 200, 3 | "asi": true, 4 | "bitwise": true, 5 | "camelcase": false, 6 | "curly": false, 7 | "eqeqeq": true, 8 | "forin": false, 9 | "immed": true, 10 | "latedef": true, 11 | "newcap": false, 12 | "noarg": false, 13 | "noempty": false, 14 | "nonew": true, 15 | "plusplus": false, 16 | "quotmark": "double", 17 | "regexp": false, 18 | "undef": true, 19 | "unused": true, 20 | "strict": false, 21 | "trailing": true, 22 | "esnext": true, 23 | "maxparams": 9, 24 | "maxdepth": 7, 25 | "maxstatements": 150, 26 | "maxlen": 200, 27 | "loopfunc": true, 28 | "-W014": false, 29 | "globals": { 30 | "window": false, 31 | "root": false, 32 | "$api": false, 33 | "_api": false, 34 | "$major": false, 35 | "$minor": false, 36 | "$micro": false, 37 | "$date": false, 38 | "include": false, 39 | "module": false, 40 | "require": false, 41 | "describe": false, 42 | "it": false, 43 | "expect": false, 44 | "global": false, 45 | "chai": false, 46 | "__dirname": false, 47 | "process": false, 48 | "console": false, 49 | "BindingJS": false 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/core/dsl/parser.bind: -------------------------------------------------------------------------------- 1 | /* 2 | ** foo-view.bind -- BindingJS view binding for "foo" mask 3 | */ 4 | 5 | @binding foo { 6 | div[data-name="pos"] (length(data:persons) > 0) { 7 | li (@person, @k: slice(sort("name", data:persons), param:window.begin, param:window.end)) { 8 | attr:data-id <- @person.id; 9 | span[data-name="num"] { 10 | text <- sprintf('%03d', param:window.begin + @k) 11 | } 12 | span[data-name="intro"] { 13 | text <- data:intro 14 | } 15 | input { 16 | @p <- @person // just alias person 17 | on:change, on:keypress +> value <-> @p.name 18 | class:error <- validate(/^.+\s+.+$/) <- @p.name 19 | attr:disabled <- !@p.enabled 20 | css:color <- @p.enabled ? "green" : "red" 21 | } 22 | button { 23 | attr:name <~ i18n(lang = param:lang, id = "kill-person") 24 | on:click +> true -> @person.kill 25 | } 26 | } 27 | button[data-name="kill"] { 28 | attr:name <- i18n(param:lang, "kill-persons") 29 | on:click +> true -> event:kill-persons 30 | } 31 | button[data-name="new"] { 32 | attr:name <- i18n(param:lang, "new-persons") 33 | on:click +> true -> event:new-persons 34 | } 35 | } 36 | div[data-name="neg"] (length(data:persons) == 0) { 37 | text <~ i18n(param:lang, "no-persons-available") 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* provide exception swallowing */ 11 | global.swallow = function (thrower) { try { thrower() } catch (e) {} } 12 | 13 | /* provide mocking functionality */ 14 | global.sinon = require("sinon") 15 | 16 | /* provide assertion functionality (base features) */ 17 | global.chai = require("chai") 18 | global.should = require("chai").should() 19 | global.expect = require("chai").expect 20 | global.assert = require("chai").assert 21 | 22 | /* provide assertion functionality (extra features) */ 23 | chai.use(require("sinon-chai")) 24 | chai.use(require("chai-fuzzy")) 25 | chai.use(require("chai-factories")) 26 | chai.use(require("chai-things")) 27 | chai.use(require("chai-interface")) 28 | 29 | /* print stack traces on assertion failures */ 30 | chai.config.includeStack = true 31 | 32 | /* load either instrumented or regular library */ 33 | let path = require("path") 34 | let load = function (name) { 35 | return process.env.COVERAGE_INSTRUMENTED ? 36 | require(path.join(__dirname, "../../../cov/" + name + ".js")) : 37 | require(path.join(__dirname, "../../stage3/src/core/" + name + ".js")) 38 | } 39 | 40 | /* load all library parts */ 41 | global.BindingJS = load("binding") 42 | 43 | -------------------------------------------------------------------------------- /examples/Calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BindingJS - Calculator 5 | 6 | 7 | 8 | 9 | 22 | 38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 |
46 |
47 | 48 | -------------------------------------------------------------------------------- /test/selenium/4-identification.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | 38 | 39 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /test/core/api/viewDataBinding.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | describe("api/viewDataBinding.js", () => { 11 | describe("binding", () => { 12 | it("Should set the binding", () => { 13 | let viewDataBinding = BindingJS.create() 14 | viewDataBinding.binding("@binding foo {}") 15 | /* jshint -W024 */ 16 | /* jshint expr:true */ 17 | expect(viewDataBinding.vars.ast).to.exist 18 | }) 19 | }) 20 | describe("template", () => { 21 | it("Should set the template", () => { 22 | let jQuery = (e) => { return { clone: () => { return e } } } 23 | BindingJS.$(jQuery) 24 | let viewDataBinding = BindingJS.create() 25 | viewDataBinding.template("
") 26 | /* jshint -W024 */ 27 | /* jshint expr:true */ 28 | expect(viewDataBinding.vars.template).to.exist 29 | }) 30 | }) 31 | describe("model", () => { 32 | it("Should set the model", () => { 33 | let viewDataBinding = BindingJS.create() 34 | let model = {} 35 | viewDataBinding.model(model) 36 | /* jshint -W024 */ 37 | /* jshint expr:true */ 38 | expect(viewDataBinding.vars.model).to.equal(model) 39 | }) 40 | }) 41 | }) 42 | 43 | -------------------------------------------------------------------------------- /src/core/adapter/attr.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | 11 | $api.plugin("attr", ($api, _api) => { 12 | return { 13 | getPaths: (element, path) => { 14 | return [path] 15 | }, 16 | 17 | getValue: (element, path) => { 18 | if (path.length !== 1) { 19 | throw _api.util.exception("attr requires a Qualifier") 20 | } 21 | 22 | let name = path[0] 23 | switch (name) { 24 | case "checked": 25 | return element.prop("checked") 26 | default: 27 | return element.attr(name) 28 | } 29 | }, 30 | 31 | set: (element, path, value) => { 32 | if (path.length !== 1) { 33 | throw _api.util.exception("attr requires a Qualifier") 34 | } 35 | 36 | let name = path[0] 37 | switch (name) { 38 | case "checked": 39 | element.prop("checked", value ? true : false) 40 | break 41 | default: 42 | element.attr(name, value) 43 | break 44 | } 45 | }, 46 | 47 | type: () => { 48 | return "view" 49 | } 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /test/selenium/10-initiator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 40 | 41 | 42 |
43 | 44 | 45 |
46 | 47 | -------------------------------------------------------------------------------- /src/core/util/jQuery.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* 11 | ** Returns a unique jquery selector for $element 12 | ** Taken from http://stackoverflow.com/questions/2068272/getting-a-jquery-selector-for-an-element 13 | */ 14 | _api.util.jQuery.getPath = (context, element) => { 15 | let $current = $api.$()(element) 16 | let $context = $api.$()(context) 17 | let path = [] 18 | let realpath = ""; 19 | // Since this code is taken from the web, we limit the number of iterations to 20 | // avoid hard to track errors 21 | let iterations = 0 22 | while (!$current.is($context)) { 23 | let index = $current.parent().find($current.prop("tagName")).index($current); 24 | let name = $current.prop("tagName"); 25 | let selector = " " + name + ":eq(" + index + ") "; 26 | path.push(selector); 27 | $current = $current.parent(); 28 | 29 | iterations++ 30 | // Happens if element is not a descendant of 31 | // context 32 | _api.util.assume(iterations < 1000) 33 | } 34 | while (path.length !== 0) { 35 | realpath += path.pop(); 36 | } 37 | return realpath; 38 | } 39 | 40 | /* 41 | ** Returns the outer HTML as a string of a jQuery element without changing it 42 | */ 43 | _api.util.jQuery.outerHtml = (jQuery) => { 44 | return $api.$()(jQuery).clone().wrap("
").parent().html() 45 | } -------------------------------------------------------------------------------- /grunt/grunt-2-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* build environment */ 13 | grunt.config.merge({ 14 | jshint: { 15 | "grunt": [ "Gruntfile.js", "grunt/*.js" ] 16 | }, 17 | eslint: { 18 | "grunt": [ "Gruntfile.js", "grunt/*.js" ] 19 | }, 20 | jsonlint: { 21 | "grunt": { 22 | src: [ 23 | "package.json", 24 | "grunt/lint-1-jshint.json", 25 | "grunt/lint-2-eslint.json" 26 | ] 27 | } 28 | }, 29 | mkdir: { 30 | "grunt": { 31 | options: { 32 | create: [ 33 | "build/stage1", 34 | "build/stage2", 35 | "build/stage3", 36 | "build/stage4" 37 | ] 38 | } 39 | } 40 | }, 41 | clean: { 42 | "grunt": [ "build", "cov" ] 43 | } 44 | }) 45 | 46 | /* common task aliasing */ 47 | grunt.registerTask("grunt-build", [ 48 | "jshint:grunt", 49 | "eslint:grunt", 50 | "jsonlint:grunt", 51 | "mkdir:grunt" 52 | ]) 53 | grunt.registerTask("grunt-clean", [ 54 | "clean:grunt" 55 | ]) 56 | } 57 | 58 | -------------------------------------------------------------------------------- /test/core/dsl/parser.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | describe("dsl/parser.js", function () { 11 | let path = require("path") 12 | let fs = require("fs") 13 | let chalk = require("chalk") 14 | describe("parse", function () { 15 | it("Allows the binding DSL to be parsed", () => { 16 | let dsl = fs.readFileSync( 17 | path.join(__dirname, "parser.bind"), 18 | { encoding: "utf8" }) 19 | let ast = BindingJS.internal().dsl.parser.parser(dsl, "spec") 20 | if (ast.error !== null) { 21 | let e = ast.error 22 | let prefix1 = "line " + e.line + " (col " + e.column + "): " 23 | let prefix2 = "" 24 | for (let i = 0; i < prefix1.length + e.location.prolog.length; i++) 25 | prefix2 += "-" 26 | throw new Error("PARSING FAILED\n" + 27 | chalk.black("ERROR: ") + prefix1 + e.location.prolog + 28 | chalk.bold(chalk.red(e.location.token)) + e.location.epilog + "\n" + 29 | chalk.black("ERROR: ") + chalk.bold(chalk.red(prefix2 + "^")) + "\n" + 30 | chalk.black("ERROR: ") + chalk.red(e.message) + "\n" 31 | ) 32 | } 33 | expect(ast).to.have.keys([ "ast", "error" ]) 34 | expect(ast).to.be.a("object") 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /grunt/grunt-6-cov.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* coverage testing */ 13 | grunt.config.merge({ 14 | env: { 15 | "instrumented": { 16 | "COVERAGE_INSTRUMENTED": "true" 17 | } 18 | }, 19 | instrument: { 20 | files: [ "build/stage3/src/core/binding.js" ], 21 | options: { 22 | lazy: true, 23 | basePath: "cov/", 24 | flatten: true 25 | } 26 | }, 27 | storeCoverage: { 28 | options: { 29 | dir: "cov/report" 30 | } 31 | }, 32 | makeReport: { 33 | src: "cov/report/**/*.json", 34 | options: { 35 | type: "lcov", 36 | dir: "cov/report", 37 | print: "detail" 38 | } 39 | }, 40 | open: { 41 | "report": { 42 | path: "cov/report/lcov-report/index.html" 43 | } 44 | }, 45 | clean: { 46 | "cover": [ "cov" ] 47 | } 48 | }) 49 | 50 | /* register coverage task */ 51 | grunt.registerTask("cover", [ 52 | "clean:cover", 53 | "instrument", 54 | "env:instrumented", 55 | "test", 56 | "storeCoverage", 57 | "makeReport" 58 | ]) 59 | } 60 | 61 | -------------------------------------------------------------------------------- /grunt/grunt-8-rel.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* global module: true */ 11 | module.exports = function (grunt) { 12 | /* release engineering */ 13 | grunt.config.merge({ 14 | "path-check": { 15 | "release": { 16 | src: [ "shtool", "tar", "gzip" ] 17 | }, 18 | "snapshot": { 19 | src: [ "shtool", "tar", "gzip" ] 20 | } 21 | }, 22 | shell: { 23 | "release": { 24 | command: "shtool tarball " + 25 | "-c 'gzip -9' " + 26 | "-e 'BindingJS-*,.git,.gitignore,node_modules,build/.done-*' " + 27 | "-o BindingJS-<%= version_string %>.tar.gz " + 28 | "." 29 | }, 30 | "snapshot": { 31 | command: "shtool tarball " + 32 | "-c 'gzip -9' " + 33 | "-e 'BindingJS-*,.git,.gitignore,node_modules,build/.done-*' " + 34 | "-o BindingJS-SNAPSHOT.tar.gz " + 35 | "." 36 | } 37 | } 38 | }) 39 | 40 | /* register tasks */ 41 | grunt.registerTask("release", [ 42 | "build", 43 | "path-check:release", 44 | "shell:release" 45 | ]) 46 | grunt.registerTask("snapshot", [ 47 | "build", 48 | "path-check:snapshot", 49 | "shell:snapshot" 50 | ]) 51 | } 52 | 53 | -------------------------------------------------------------------------------- /test/selenium/example-calculator.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | example-Calculator 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 |
example-Calculator
open${baseUrl}/examples/Calculator/index.html
assertValuecss=#factor14
assertValuecss=#factor27
assertTextcss=#result28
typecss=#factor112
assertTextcss=#result84
typecss=#factor19
assertTextcss=#result63
getEvalwindow.model.factor1 = 2
assertTextcss=#result14
getEvalwindow.model.factor2 = 3
assertTextcss=#result6
75 | 76 | 77 | -------------------------------------------------------------------------------- /test/selenium/12-parameter.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12-parameter 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 |
12-parameter
open${baseUrl}/test/selenium/12-parameter.html
getEvalwindow.model.firstname = "foo"
getEvalwindow.model.lastname = "bar"
assertNotValuecss=inputfoo bar
waitForValuecss=inputfoo bar
getEvalwindow.model.concatSymbol = "."
waitForValuecss=inputfoo.bar
assertAttributecss=input@data-hash31
typecss=input  foo  
assertAttributecss=input@data-hash97
getEvalwindow.model.trim = true
assertAttributecss=input@data-hash22
76 | 77 | 78 | -------------------------------------------------------------------------------- /src/core/util/map.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* 11 | ** JavaScript objects only support strings as keys 12 | */ 13 | class Map { 14 | 15 | constructor () { 16 | this.keys = [] 17 | this.values = [] 18 | } 19 | 20 | /* 21 | ** Returns a (shallow) copy of all keys stored in this map 22 | */ 23 | getKeys () { 24 | return _api.util.array.clone(this.keys) 25 | } 26 | 27 | /* 28 | ** Checks if the map holds an element with the given key 29 | */ 30 | hasKey (key) { 31 | return _api.util.array.contains(this.keys, key) 32 | } 33 | 34 | /* 35 | ** Sets the map at a given key to a given value 36 | ** If the key was set previously, its value is overwritten 37 | */ 38 | set (key, value) { 39 | if (this.hasKey(key)) { 40 | this.values[this.keys.indexOf(key)] = value 41 | } else { 42 | this.keys.push(key) 43 | this.values.push(value) 44 | } 45 | } 46 | 47 | /* 48 | ** Returns the value stored at a given key or undefined if no such key 49 | */ 50 | get (key) { 51 | if (this.hasKey(key)) { 52 | return this.values[this.keys.indexOf(key)] 53 | } 54 | } 55 | 56 | /* 57 | ** Removes the given key together with its value from the map 58 | */ 59 | remove (key) { 60 | if (this.hasKey(key)) { 61 | _api.util.array.remove(this.values, this.values[this.keys.indexOf(key)]) 62 | _api.util.array.remove(this.keys, key) 63 | } 64 | } 65 | 66 | } 67 | 68 | _api.util.Map = Map 69 | -------------------------------------------------------------------------------- /test/selenium/9-abort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | -------------------------------------------------------------------------------- /test/selenium/5-cycle-detection.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | -------------------------------------------------------------------------------- /test/selenium/bindingjs.seleniumsuite: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Suite 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
Test Suite
setEnv
1-binding
2-iteration-1
2-iteration-2
2-iteration-3
2-iteration-4
3-sockets
4-identification
6-deactivate
7-pause
8-when
9-abort
10-initiator
11-sequence
12-parameter
13-expression
14-unmount
15-iterationUpdate
16-lambda
example-calculator
example-clock
example-TodoMVC
34 | 35 | 36 | -------------------------------------------------------------------------------- /test/selenium/15-iterationUpdate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 45 | 46 | 47 |
48 |
49 |
    50 |
  • 51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /src/core/connector/sort.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | // Usage 1: sort("firstname", "-age") 11 | // Usage 2: sort(a, b => a.age > b.age) 12 | $api.plugin("sort", ($api, _api) => { 13 | return { 14 | process: (input, params) => { 15 | input = input instanceof Array ? input : [input] 16 | if (params.length === 1 && typeof params[0] === "function") { 17 | // Usage 2 18 | let sortFn = params[0] 19 | input.sort((a, b) => { 20 | let aVal = _api.util.convertToValues(a) 21 | let bVal = _api.util.convertToValues(b) 22 | return sortFn(aVal, bVal) 23 | }) 24 | } else if (params.length > 0) { 25 | // Usage 1 26 | for (var i = params.length - 1; i >= 0; i--) { 27 | let attribute = _api.util.convertIfReference(params[i]) 28 | if (typeof attribute !== "string" || params[i] === "") { 29 | throw _api.util.exception("Cannot sort by " + attribute) 30 | } 31 | let descending = params[i].charAt(0) === "-" 32 | attribute = descending ? attribute.substr(1) : attribute 33 | input.sort((a, b) => { 34 | let aVal = _api.util.convertToValues(a) 35 | let bVal = _api.util.convertToValues(b) 36 | let greater = descending ? aVal[attribute] < bVal[attribute] : aVal[attribute] > bVal[attribute] 37 | let equal = aVal[attribute] === bVal[attribute] 38 | return equal ? 0 : (greater ? 1 : -1) 39 | }) 40 | } 41 | } else { 42 | // Without parameter 43 | input.sort() 44 | } 45 | return input 46 | } 47 | } 48 | }) 49 | -------------------------------------------------------------------------------- /test/selenium/13-expression.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13-expression 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 |
13-expression
open${baseUrl}/test/selenium/13-expression.html
assertTextid=s1foo.bar.baz.quux.qux
assertTextid=s216
assertTextid=s314
assertTextid=s40.5
assertTextid=s5yadda
assertTextid=s6qux
assertTextid=s7true
assertTextid=s8true
assertTextid=s9true
assertTextid=s10false
assertTextid=s11true
assertTextid=s12found
assertTextid=s13foundToo
assertTextid=s14bar
assertTextid=s15found
assertTextid=s160
assertTextid=s17{}
105 | 106 | 107 | -------------------------------------------------------------------------------- /src/umd/umd-library-prolog.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License, v. 2.0. If a copy of the MPL was not distributed with this 7 | ** file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* Universal Module Definition (UMD) for Library */ 11 | (function (root, name, factory) { 12 | /* global define: false */ 13 | /* global module: false */ 14 | //TODO: Evaluate, In what ways the library can be used outside a browser 15 | var export_type = root[name.replace(/[^a-zA-Z0-9_]/g, "_") + "_export"]; 16 | if ( ( typeof define === "function" 17 | && typeof define.amd !== "undefined" 18 | && typeof export_type === "undefined") 19 | || ( typeof export_type !== "undefined" 20 | && export_type === "AMD" )) 21 | /* AMD environment */ 22 | define(name, function () { 23 | return factory(root); 24 | }); 25 | else if ( 26 | ( typeof module === "object" 27 | && typeof module.exports === "object" 28 | && typeof export_type === "undefined") 29 | || ( typeof export_type !== "undefined" 30 | && export_type === "CommonJS" )) 31 | /* CommonJS environment */ 32 | module.exports = factory(root); 33 | else { 34 | /* Browser environment */ 35 | var api = factory(root); 36 | api.symbol = (function () { 37 | var symbol_name = null; 38 | var symbol_value; 39 | return function (symbol) { 40 | if (symbol_name !== null) { 41 | root[symbol_name] = symbol_value; 42 | symbol_name = null; 43 | } 44 | if (arguments.length === 1) { 45 | symbol_name = symbol; 46 | symbol_value = root[symbol_name]; 47 | root[symbol_name] = api; 48 | } 49 | return api; 50 | }; 51 | })(); 52 | api.symbol(name); 53 | } 54 | }(/* global global: false */ 55 | (typeof global !== "undefined" ? global : 56 | /* global window: false */ 57 | (typeof window !== "undefined" ? window : this)), "BindingJS", function (root) { 58 | /* define internal and external API */ 59 | var _api = {}; 60 | var $api = {}; 61 | -------------------------------------------------------------------------------- /test/selenium/16-lambda.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16-lambda 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 |
16-lambda
open${baseUrl}/test/selenium/16-lambda.html
assertTextid=s10 2 4 6
assertTextid=s20 2 8 18 32 50 72 98 128 162
assertTextid=s84
getEvalwindow.model.mod =  3
assertTextid=s10 3 6
assertTextid=s20 3 12 27 48 75 108 147 192 243
assertTextid=s83
assertTextid=s3Charlie Bob Alice
assertTextid=s4Charlie Alice Bob
assertTextid=s5Bob Alice Charlie
assertTextid=s6Charlie Bob Alice
assertTextid=s7Charlie Bob Alice
assertTextid=s9true
assertTextid=s10false
assertTextid=s11true
assertTextid=s12false
assertTextid=s13true
assertTextid=s14false
111 | 112 | 113 | -------------------------------------------------------------------------------- /test/selenium/3-sockets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 18 | 51 | 52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bindingjs", 3 | "description": "View Data Binding for JavaScript", 4 | "keywords": [ "view", "data", "binding" ], 5 | "version": "0.7.0", 6 | "contributors": [ 7 | { 8 | "name": "Johannes Rummel", 9 | "email": "johannes@rummel.cc", 10 | "url": "http://www.rummel.cc/" 11 | }, 12 | { 13 | "name": "Ralf S. Engelschall", 14 | "email": "rse@engelschall.com", 15 | "url": "http://engelschall.com/" 16 | } 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/rummelj/bindingjs.git" 21 | }, 22 | "licenses": [{ 23 | "type": "MPL/2.0", 24 | "url": "http://mozilla.org/MPL/2.0/" 25 | }], 26 | "homepage": "https://github.com/rummelj/bindingjs", 27 | "bugs": "https://github.com/rummelj/bindingjs/issues", 28 | "readmeFilename": "README.md", 29 | "bin": { 30 | }, 31 | "dependencies": { 32 | }, 33 | "devDependencies": { 34 | "load-grunt-tasks": "~0.4.0", 35 | "grunt": "~0.4.5", 36 | "grunt-cli": "~0.1.13", 37 | "grunt-contrib-jshint": "~0.10.0", 38 | "grunt-contrib-uglify": "~0.4.0", 39 | "grunt-contrib-clean": "~0.5.0", 40 | "grunt-contrib-watch": "~0.6.1", 41 | "grunt-contrib-copy": "~0.5.0", 42 | "grunt-expand-include": "~0.9.6", 43 | "grunt-shell": "~0.7.0", 44 | "grunt-mkdir": "~0.1.1", 45 | "grunt-touch": "~0.1.0", 46 | "grunt-newer-explicit": "~0.9.3", 47 | "grunt-path-check": "~0.9.3", 48 | "grunt-env": "~0.4.1", 49 | "grunt-open": "~0.2.3", 50 | "grunt-complexity": "~0.1.7", 51 | "grunt-eslint": "~0.5.0", 52 | "grunt-mocha-test": "~0.11.0", 53 | "grunt-istanbul": "~0.3.0", 54 | "grunt-jsonlint": "~1.0.4", 55 | "grunt-peg": "~1.3.1", 56 | "grunt-es6-transpiler": "~0.1.0", 57 | "chai": "~1.9.1", 58 | "chai-factories": "~0.1.0", 59 | "chai-fuzzy": "~1.4.0", 60 | "chai-things": "~0.2.0", 61 | "chai-interface": "~1.1.0", 62 | "sinon-chai": "~2.5.0", 63 | "chalk": "~0.4.0", 64 | "node-fs": "~0.1.7" 65 | }, 66 | "engines": { 67 | "node": ">=0.10.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/core/engine/sockets.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** viewDataBindingJS -- View Data viewDataBinding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.engine.sockets.callRemoval = (viewDataBinding, expItNode) => { 11 | _api.util.each(expItNode.get("instances"), (instance) => { 12 | _api.engine.sockets.callRemovalInstance(viewDataBinding, expItNode, instance) 13 | }) 14 | _api.util.each(expItNode.childs(), (child) => { 15 | _api.engine.sockets.callRemoval(viewDataBinding, child) 16 | }) 17 | } 18 | 19 | _api.engine.sockets.callInsertion = (viewDataBinding, expItNode) => { 20 | _api.util.each(expItNode.get("instances"), (instance) => { 21 | _api.engine.sockets.callInsertionInstance(viewDataBinding, expItNode, instance) 22 | }) 23 | _api.util.each(expItNode.childs(), (child) => { 24 | _api.engine.sockets.callInsertion(viewDataBinding, child) 25 | }) 26 | } 27 | 28 | _api.engine.sockets.callRemovalInstance = (viewDataBinding, expItNode, instance) => { 29 | if (instance.sockets.length > 0) { 30 | let keys = _api.engine.sockets.getKeys(expItNode, instance) 31 | _api.util.each(instance.sockets, (socket) => { 32 | _api.util.each(viewDataBinding.vars.socketRemovalObserver[socket.id], (callback) => { 33 | callback(keys, socket.element) 34 | }) 35 | }) 36 | } 37 | } 38 | 39 | _api.engine.sockets.callInsertionInstance = (viewDataBinding, expItNode, instance) => { 40 | if (instance.sockets.length > 0) { 41 | let keys = _api.engine.sockets.getKeys(expItNode, instance) 42 | _api.util.each(instance.sockets, (socket) => { 43 | _api.util.each(viewDataBinding.vars.socketInsertionObserver[socket.id], (callback) => { 44 | callback(keys, socket.element) 45 | }) 46 | }) 47 | } 48 | } 49 | 50 | _api.engine.sockets.getKeys = (expItNode, instance) => { 51 | let keys = [] 52 | // Do not add key, if this is the root expItNode 53 | if (expItNode.getParent()) { 54 | keys.push(instance.key) 55 | } 56 | // If not expItNode.getParent().getParent() means, that expItNode.get("instance") refers to the instance of root 57 | while (expItNode.getParent() && expItNode.getParent().getParent() && expItNode.get("instance")) { 58 | keys.push(expItNode.get("instance").key) 59 | expItNode = expItNode.getParent() 60 | } 61 | return keys 62 | } -------------------------------------------------------------------------------- /test/selenium/15-iterationUpdate.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 15-iterationUpdate 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
15-iterationUpdate
open${baseUrl}/test/selenium/15-iterationUpdate.html
assertTextcss=li.number0
assertText//div[@id='template']/div/ul/li[2]2
assertText//div[@id='template']/div/ul/li[3]4
assertText//div[@id='template']/div/ul/li[5]8
getEvalwindow.model.how = 3
assertTextcss=li.number0
assertText//div[@id='template']/div/ul/li[2]3
assertText//div[@id='template']/div/ul/li[3]6
assertText//div[@id='template']/div/ul/li[4]9
getEvalwindow.model.how = 1
assertTextcss=li.number0
assertText//div[@id='template']/div/ul/li[2]1
assertText//div[@id='template']/div/ul/li[3]2
assertText//div[@id='template']/div/ul/li[4]3
assertText//div[@id='template']/div/ul/li[5]4
assertText//div[@id='template']/div/ul/li[6]5
assertText//div[@id='template']/div/ul/li[7]6
assertText//div[@id='template']/div/ul/li[8]7
assertText//div[@id='template']/div/ul/li[9]8
assertText//div[@id='template']/div/ul/li[10]9
121 | 122 | 123 | -------------------------------------------------------------------------------- /test/selenium/11-sequence.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11-sequence 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
11-sequence
open${baseUrl}/test/selenium/11-sequence.html
typecss=#i1foo
verifyEvalwindow.model.i1Firstfoo
verifyEvalwindow.model.i1Secondfoo
getEvalwindow.model.i2 = "foo"
getEvalwindow.model.i3 = "bar"
getEvalwindow.model.i4 = "baz"
assertTextcss=#s2["foo","bar","baz"]
assertTextcss=#s3foobarbaz
getEvalwindow.model.i3 = "quux"
assertTextcss=#s3fooquuxbaz
assertValuecss=#i4baz
assertAttributecss=#i4@data-hash8
getEvalwindow.model.i4 = "longString"
assertValuecss=#i4longString
assertAttributecss=#i4@data-hash108
assertValuecss=#i56
assertAttributecss=#i5@data-min3
assertAttributecss=#i5@data-max10
getEvalwindow.model.num3 = 1
assertValuecss=#i53
assertAttributecss=#i5@data-min1
assertAttributecss=#i5@data-max5
131 | 132 | 133 | -------------------------------------------------------------------------------- /test/core/api/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | describe("api/api.js", () => { 11 | describe("create", () => { 12 | it("Should return a new instance", () => { 13 | let _api = BindingJS.internal() 14 | let viewDataBinding = BindingJS.create() 15 | expect(viewDataBinding).instanceof(_api.ViewDataBinding) 16 | }) 17 | }) 18 | describe("$", () => { 19 | it("Should throw if jQuery is accessed in node.js env", () => { 20 | expect(BindingJS.$).to.throw(Error) 21 | }) 22 | it("Should set jQuery to a new value", () => { 23 | let jQuery = () => {} 24 | BindingJS.$(jQuery) 25 | expect(BindingJS.$()).to.equal(jQuery) 26 | }) 27 | }) 28 | describe("debug", () => { 29 | it("Should return the debug level", () => { 30 | // Checks default 31 | expect(BindingJS.debug()).to.equal(9) 32 | }) 33 | it("Should set a new debug level", () => { 34 | BindingJS.debug(8) 35 | expect(BindingJS.debug()).to.equal(8) 36 | }) 37 | }) 38 | describe("plugin", () => { 39 | it("Should allow to register Adapter", () => { 40 | let adapter = { 41 | getValue: () => {}, 42 | getPaths: () => {}, 43 | type: () => { return "model" } 44 | } 45 | let factory = () => { 46 | return adapter 47 | } 48 | BindingJS.plugin("foo", factory) 49 | expect(BindingJS.plugin("foo")).to.equal(adapter) 50 | }) 51 | it("Should allow to register Connector", () => { 52 | let connector = { 53 | process: () => {} 54 | } 55 | let factory = () => { 56 | return connector 57 | } 58 | BindingJS.plugin("bar", factory) 59 | expect(BindingJS.plugin("bar")).to.equal(connector) 60 | }) 61 | }) 62 | describe("version", () => { 63 | it("Should return reasonable structure", () => { 64 | expect(BindingJS.version).to.have.keys([ "major", "minor", "micro", "date" ]) 65 | expect(BindingJS.version.major).to.be.a("number").least(0) 66 | expect(BindingJS.version.minor).to.be.a("number").least(0) 67 | expect(BindingJS.version.micro).to.be.a("number").least(0) 68 | expect(BindingJS.version.date ).to.be.a("number").least(19700101) 69 | }) 70 | }) 71 | describe("abortSymbol", () => { 72 | it("Should be defined", () => { 73 | // Not so great chai here: https://github.com/chaijs/chai/issues/41 74 | /* jshint -W024 */ 75 | /* jshint expr:true */ 76 | expect(BindingJS.abortSymbol).to.exist 77 | }) 78 | }) 79 | }) 80 | 81 | -------------------------------------------------------------------------------- /test/selenium/10-initiator.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10-initiator 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 142 | 143 | 144 | 145 |
10-initiator
open${baseUrl}/test/selenium/10-initiator.html
typecss=#foofoo
typecss=#barbar
verifyEvalwindow.model.foo
verifyEvalwindow.model.bar
getEvalwindow.model.t1 = true
verifyEvalwindow.model.foofoo
verifyEvalwindow.model.barbar
typecss=#foofoo2
typecss=#barbar2
verifyEvalwindow.model.foofoo
verifyEvalwindow.model.barbar
getEvalwindow.model.t2 = true
verifyEvalwindow.model.foofoo2
getEvalwindow.model.foo = "foo3"
getEvalwindow.model.bar = "bar3"
verifyValuecss=#foofoo2
verifyValuecss=#barbar2
getEvalwindow.model.t3 = true
verifyValuecss=#foofoo3
verifyValuecss=#barbar3
getEvalwindow.model.foo = "foo4"
getEvalwindow.model.bar = "bar4"
verifyValuecss=#foofoo3
getEvalwindow.model.t4 = true
verifyValuecss=#foofoo4
146 | 147 | 148 | -------------------------------------------------------------------------------- /test/selenium/1-binding.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 1-binding 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 142 |
1-binding
open${baseUrl}/test/selenium/1-binding.html
getEvalwindow.model.m2vText = "val1"
assertTextcss=#m2vval1
typecss=#v2mval2
verifyEvalwindow.model.v2mValueval2
assertTextcss=#otm2votm2vText
getEvalwindow.model.otm2vText = "foo"
assertTextcss=#otm2votm2vText
verifyEvalwindow.model.otv2mTextotv2m
assertTextcss=#otm2vcOTM2VTEXT
verifyEvalwindow.model.otv2mcTextOTV2MC
typecss=#otv2mcfoo
verifyEvalwindow.model.otv2mcTextOTV2MC
getEvalwindow.model.twowayValue = "twoway"
assertValuecss=#twowaytwoway
typecss=#twowayfoo
verifyEvalwindow.model.twowayValuefoo
getEvalwindow.model.twowaycValue = "twoway"
assertValuecss=#twowaycTWOWAY
typecss=#twowaycfoo
verifyEvalwindow.model.twowaycValueFOO
getEvalwindow.model.twowayccValue = "twoway"
assertValuecss=#twowayccYAWOWT
typecss=#twowayccfoo
verifyEvalwindow.model.twowayccValueOOF
143 | 144 | 145 | -------------------------------------------------------------------------------- /src/core/dsl/parser.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | /* include the generated PEG parser */ 11 | _api.dsl.parser = (function () { 12 | let module = {} 13 | // Generated in grunt process 14 | include("grammar.js") 15 | return module.exports 16 | })() 17 | 18 | /* provide a helper function for unrolling the parse stack */ 19 | _api.dsl.parser.unroll = (first, list, take) => { 20 | if (typeof list !== "object" 21 | || !(list instanceof Array)) 22 | throw "unroll: invalid list argument (expected Array)" 23 | if (typeof take !== "undefined") { 24 | if (typeof take === "number") 25 | take = [ take ] 26 | let result = [] 27 | if (first !== null) 28 | result.push(first) 29 | for (let i = 0; i < list.length; i++) { 30 | for (let j = 0; j < take.length; j++) 31 | result.push(list[i][take[j]]) 32 | } 33 | return result 34 | } 35 | else { 36 | if (first !== null) 37 | list.unshift(first) 38 | return list 39 | } 40 | } 41 | 42 | /* utility function */ 43 | let excerpt = (txt, o) => { 44 | let l = txt.length 45 | let b = o - 20; if (b < 0) b = 0 46 | let e = o + 20; if (e > l) e = l 47 | let extract = (txt, pos, len) => 48 | txt.substr(pos, len).replace(/\r/g, "\\r").replace(/\n/g, "\\n") 49 | return { 50 | prolog: extract(txt, b, o - b), 51 | token: extract(txt, o, 1), 52 | epilog: extract(txt, o + 1, e - (o + 1)) 53 | } 54 | } 55 | 56 | /* provide top-level parsing functionality */ 57 | _api.dsl.parser.parser = (txt, rule) => { 58 | if (typeof rule === "undefined") 59 | rule = "spec" 60 | let result = { ast: null, error: null } 61 | try { 62 | result.ast = _api.dsl.parser.parse(txt, { startRule: rule }) 63 | } 64 | catch (e) { 65 | result.error = { 66 | line: _api.util.object.ifUndefined(e.line, 0), 67 | column: _api.util.object.ifUndefined(e.column, 0), 68 | message: e.message, 69 | found: _api.util.object.ifUndefined(e.found, ""), 70 | expected: _api.util.object.ifUndefined(e.expected, ""), 71 | location: excerpt(txt, _api.util.object.ifUndefined(e.offset, 0)) 72 | } 73 | } 74 | return result 75 | } 76 | 77 | _api.dsl.parser.safeParser = (txt, rule) => { 78 | let astWrapper = _api.dsl.parser.parser(txt, rule) 79 | if (astWrapper.error !== null) { 80 | let e = astWrapper.error 81 | let prefix1 = "line " + e.line + " (col " + e.column + "): " 82 | let prefix2 = "" 83 | for (let i = 0; i < prefix1.length + e.location.prolog.length; i++){ 84 | prefix2 += "-" 85 | } 86 | $api.debug(1, prefix1 + e.location.prolog + e.location.token + e.location.epilog) 87 | $api.debug(1, prefix2 + "^") 88 | $api.debug(1, e.message) 89 | throw _api.util.exception("Parsing failed") 90 | } else { 91 | return astWrapper.ast 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/core/util/object.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.util.object.isBoolean = (obj) => { 11 | let type = Object.prototype.toString.call(obj) 12 | return type === "[object Boolean]" 13 | } 14 | 15 | _api.util.object.isDefined = (obj) => { 16 | return typeof obj !== "undefined" 17 | } 18 | 19 | /* 20 | ** Returns obj if defined and defaultObj is obj is undefined 21 | */ 22 | _api.util.object.ifUndefined = (obj, defaultObj) => { 23 | if (typeof obj === "undefined") { 24 | return defaultObj 25 | } else { 26 | return obj 27 | } 28 | } 29 | 30 | /* 31 | ** Clones an object with the help of jQuery 32 | */ 33 | _api.util.object.clone = (obj) => { 34 | return _api.util.traverseStructure(obj, (value) => { 35 | if (_api.util.isReference(value)) { 36 | return value.clone() 37 | } else { 38 | return value 39 | } 40 | }) 41 | } 42 | 43 | /* 44 | ** Deeply compares two objects by recursively enumerating their nested 45 | ** objects and arrays. It does not consider inheritance. 46 | */ 47 | _api.util.object.equals = (a, b) => { 48 | if (typeof a !== typeof b) { 49 | return false 50 | } else { 51 | if (a instanceof Array && !(b instanceof Array) || 52 | b instanceof Array && !(a instanceof Array)) { 53 | return false 54 | } else if (a instanceof Array && b instanceof Array) { 55 | if (a.length !== b.length) { 56 | return false 57 | } else { 58 | for (let i = 0; i < a.length; i++) { 59 | if (!_api.util.object.equals(a[i], b[i])) { 60 | return false 61 | } 62 | } 63 | return true 64 | } 65 | } else if (typeof a === "object") { 66 | // Check if every key of a is in b and vice versa 67 | let aKeys = _api.util.object.getKeys(a) 68 | for (let i = 0; i < aKeys.length; i++) { 69 | if (!(aKeys[i] in b)) { 70 | return false 71 | } 72 | } 73 | let bKeys = _api.util.object.getKeys(b) 74 | for (let i = 0; i < bKeys.length; i++) { 75 | if (!(bKeys[i] in a)) { 76 | return false 77 | } 78 | } 79 | // Both keysets are equal 80 | for (let i = 0; i < aKeys.length; i++) { 81 | if (!_api.util.object.equals(a[aKeys[i]], b[bKeys[i]])) { 82 | return false 83 | } 84 | } 85 | return true 86 | } else { 87 | return a === b 88 | } 89 | } 90 | } 91 | 92 | /* 93 | ** Returns the set of keys of an obj 94 | */ 95 | _api.util.object.getKeys = (obj) => { 96 | let result = [] 97 | for (let key in obj) { 98 | if (obj.hasOwnProperty(key)) { 99 | result.push(key) 100 | } 101 | } 102 | return result 103 | } -------------------------------------------------------------------------------- /src/core/engine/bindingscope.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | class BindingScope { 11 | 12 | constructor () { 13 | this.data = {} 14 | this.observerIds = {} 15 | this.observer = {} 16 | this.observerId = 0 17 | this.paused = false 18 | this.pauseQueue = [] 19 | } 20 | 21 | pause() { 22 | this.paused = true 23 | } 24 | 25 | resume() { 26 | this.paused = false 27 | for (let i = 0; i < this.pauseQueue.length; i++) { 28 | this.notify(this.pauseQueue[i]) 29 | } 30 | this.pauseQueue = [] 31 | } 32 | 33 | getIds() { 34 | let result = [] 35 | for (let id in this.data) { 36 | result.push(id) 37 | } 38 | return result 39 | } 40 | 41 | get(id) { 42 | return this.data[id] 43 | } 44 | 45 | set(id, to) { 46 | if (this.data[id] !== to) { 47 | this.unobserveReference(id) 48 | this.data[id] = to 49 | this.notify(id) 50 | this.observeReference(id, to) 51 | } 52 | } 53 | 54 | observeReference(id, value) { 55 | if (_api.util.isReference(value) && value.isObservable()) { 56 | let observerId = value.observe(() => { 57 | if (!this.paused) { 58 | this.notify(id) 59 | } else if (this.pauseQueue.indexOf(id) === -1) { 60 | this.pauseQueue.push(id) 61 | } 62 | }) 63 | this.observerIds[id] = { reference: value, observerId: observerId } 64 | } 65 | } 66 | 67 | unobserveReference(id) { 68 | let item = this.observerIds[id] 69 | if (item && _api.util.isReference(item.reference) && item.reference.isObservable()) { 70 | item.reference.unobserve(item.observerId) 71 | } 72 | delete this.observerIds[id] 73 | } 74 | 75 | destroy(id) { 76 | // If a reference was previously in bindingScope, unobserve it 77 | this.unobserveReference(id) 78 | 79 | // Purge the id from data 80 | delete this.data[id] 81 | } 82 | 83 | observe(id, callback) { 84 | if(!this.observer[id]) { 85 | this.observer[id] = [] 86 | } 87 | this.observer[id].push({ id: this.observerId, callback: callback }) 88 | return this.observerId++ 89 | } 90 | 91 | unobserve(id) { 92 | for (let name in this.observer) { 93 | let observer = _api.util.array.findFirst(this.observer[name], (item) => { 94 | return item.id === id 95 | }) 96 | if (observer) { 97 | _api.util.array.remove(this.observer[name], observer) 98 | break; 99 | } 100 | } 101 | } 102 | 103 | notify(id) { 104 | _api.util.each(this.observer[id], (observer) => { 105 | observer.callback() 106 | }) 107 | } 108 | } 109 | 110 | /* export class */ 111 | _api.engine.BindingScope = BindingScope 112 | -------------------------------------------------------------------------------- /test/selenium/2-iteration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 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 | -------------------------------------------------------------------------------- /test/selenium/12-parameter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 74 | 75 | 76 |
77 | 78 |
79 | 80 | -------------------------------------------------------------------------------- /src/core/engine/reference.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | class Reference { 11 | 12 | constructor (adapter, path, parameters) { 13 | this.adapter = adapter 14 | this.path = path 15 | this.parameters = parameters 16 | } 17 | 18 | getElement () { 19 | return this.element 20 | } 21 | 22 | setElement (element) { 23 | this.element = element 24 | } 25 | 26 | getModel () { 27 | return this.model 28 | } 29 | 30 | setModel (model) { 31 | this.model = model 32 | } 33 | 34 | getAdapter () { 35 | return this.adapter 36 | } 37 | 38 | getPath () { 39 | return this.path 40 | } 41 | 42 | setPath (path) { 43 | this.path = path 44 | } 45 | 46 | isObservable () { 47 | return _api.util.object.isDefined(this.adapter.observe) && typeof this.adapter.observe === "function" 48 | } 49 | 50 | observe (callback) { 51 | let observerId = 0 52 | if (this.adapter.type() === "model") { 53 | observerId = this.adapter.observe(this.model, this.path, this.parameters, callback) 54 | } else if (this.adapter.type() === "view") { 55 | observerId = this.adapter.observe(this.element, this.path, this.parameters, callback) 56 | } else { 57 | throw _api.util.exception("Unknown adapter type: " + this.adapter.type()) 58 | } 59 | return observerId 60 | } 61 | 62 | unobserve (observerId) { 63 | this.adapter.unobserve(observerId) 64 | } 65 | 66 | getValue() { 67 | if (this.adapter.type() === "model") { 68 | return this.adapter.getValue(this.model, this.path, this.parameters) 69 | } else if (this.adapter.type() === "view") { 70 | return this.adapter.getValue(this.element, this.path, this.parameters) 71 | } 72 | } 73 | 74 | set (value) { 75 | if (this.adapter.type() === "model") { 76 | this.adapter.set(this.model, this.path, value, this.parameters) 77 | } else if (this.adapter.type() === "view") { 78 | this.adapter.set(this.element, this.path, value, this.parameters) 79 | } 80 | } 81 | 82 | type () { 83 | return this.adapter.type() 84 | } 85 | 86 | clone () { 87 | let result = new Reference(this.adapter, this.path.slice(0), this.parameters) 88 | result.setModel(this.model) 89 | result.setElement(this.element) 90 | return result 91 | } 92 | 93 | cloneAndAddToPath (elem) { 94 | let newPath = this.path.slice(0) 95 | if (elem instanceof Array) { 96 | for (let i = 0; i < elem.length; i++) { 97 | newPath.push(elem[i]) 98 | } 99 | } else { 100 | newPath.push(elem) 101 | } 102 | let result = new Reference(this.adapter, newPath, this.parameters) 103 | result.setModel(this.model) 104 | result.setElement(this.element) 105 | return result 106 | } 107 | } 108 | 109 | /* export class */ 110 | _api.engine.binding.Reference = Reference 111 | -------------------------------------------------------------------------------- /test/selenium/6-deactivate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 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 | -------------------------------------------------------------------------------- /src/core/adapter/value.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | $api.plugin("value", ($api, _api) => { 11 | class ValueAdapter { 12 | 13 | constructor () { 14 | this.observer = new _api.util.Map() 15 | this.observerCounter = new _api.util.Counter() 16 | } 17 | 18 | notify (element) { 19 | _api.util.each(this.observer.get(element), (observer) => { 20 | observer.callback() 21 | }) 22 | } 23 | 24 | observe (element, path, params, callback) { 25 | _api.util.assume(path.length === 0) 26 | if (!this.observer.hasKey(element)) { 27 | this.observer.set(element, []) 28 | let self = this 29 | element.on("change input propertychange paste", () => { self.notify(element) }) 30 | } 31 | let observerId = this.observerCounter.getNext() 32 | this.observer.get(element).push({ observerId: observerId, callback: callback }) 33 | return observerId 34 | } 35 | 36 | unobserve (observerId) { 37 | let element = _api.util.array.findFirst(this.observer.getKeys(), (key) => { 38 | return _api.util.array.ifAny(this.observer.get(key), (observer) => { 39 | return observer.observerId === observerId 40 | }) 41 | }) 42 | let observer = _api.util.array.findFirst(this.observer.get(element), (observer) => { 43 | return observer.observerId === observerId 44 | }) 45 | if (_api.util.object.isDefined(element) && _api.util.object.isDefined(observer)) { 46 | _api.util.array.remove(this.observer.get(element), observer) 47 | if (this.observer.get(element).length === 0) { 48 | element.off("change input propertychange paste") 49 | this.observer.remove(element) 50 | } 51 | } else { 52 | $api.debug(5, "Warning: Tried to unobserve observer, which was not there") 53 | } 54 | } 55 | 56 | getPaths (element, path) { 57 | return [path] 58 | } 59 | 60 | getValue (element, path, params) { 61 | let value = element.val() 62 | if (params && params.trim) { 63 | value = value.trim() 64 | } 65 | return value 66 | } 67 | 68 | set (element, path, value, params) { 69 | let oldValue = element.val() 70 | 71 | if (params && params.typeKeys) { 72 | params.delay = params.delay ? params.delay : 50 73 | for (let i = 0; i < value.length; i++) { 74 | let j = i 75 | /* global setTimeout */ 76 | setTimeout(() => { 77 | element.val(value.substr(0, j + 1)) 78 | }, params.delay * j) 79 | } 80 | } else { 81 | element.val(value) 82 | } 83 | 84 | if (value !== oldValue) { 85 | element.trigger("change") 86 | } 87 | } 88 | 89 | type () { 90 | return "view" 91 | } 92 | } 93 | 94 | return new ValueAdapter() 95 | }) 96 | -------------------------------------------------------------------------------- /test/selenium/14-unmount.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 18 | 72 | 73 | 74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 88 | -------------------------------------------------------------------------------- /test/selenium/16-lambda.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 52 | 73 | 74 | 75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | 90 |
91 | 92 | -------------------------------------------------------------------------------- /test/selenium/11-sequence.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 87 | 88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | -------------------------------------------------------------------------------- /test/selenium/2-iteration-4.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 2-iteration-4 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
2-iteration-4
open${baseUrl}/test/selenium/2-iteration.html
assertTextcss=div.keyFourtho
assertText//div[@id='template']/div[2]/div/div/div[2]p
assertText//div[@id='template']/div[2]/div/div[2]/divq
assertText//div[@id='template']/div[2]/div/div[2]/div[2]r
assertText//div[@id='template']/div[2]/div[2]/div/divs
assertText//div[@id='template']/div[2]/div[2]/div/div[2]t
assertText//div[@id='template']/div[2]/div[2]/div[2]/divu
assertText//div[@id='template']/div[2]/div[2]/div[2]/div[2]v
assertAttributecss=div.keySecond:nth-child(1)@data-keya
assertAttributecss=div.keySecond:nth-child(2)@data-keyb
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(1)@data-keyc
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(2)@data-keyd
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(1)@data-keye
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(2)@data-keyf
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(1) > div:nth-child(1)@data-keyg
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(1) > div:nth-child(2)@data-keyh
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(2) > div:nth-child(1)@data-keyi
assertAttributecss=div.keySecond:nth-child(1) > div:nth-child(2) > div:nth-child(2)@data-keyj
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(1) > div:nth-child(1)@data-keyk
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(1) > div:nth-child(2)@data-keyl
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(2) > div:nth-child(1)@data-keym
assertAttributecss=div.keySecond:nth-child(2) > div:nth-child(2) > div:nth-child(2)@data-keyn
130 | 131 | 132 | -------------------------------------------------------------------------------- /test/selenium/3-sockets.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 3-slots 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 |
3-slots
open${baseUrl}/test/selenium/3-sockets.html
assertTextcss=spanPlainSocket
assertTextcss=span.keys0 - 0
assertText//div[@id='template']/div[2]/div/div[2]/div/div/span1 - 0
assertText//div[@id='template']/div[2]/div[2]/div/div/div/span0 - 1
assertText//div[@id='template']/div[2]/div[2]/div[2]/div/div/span1 - 1
verifyEvalwindow.binding.socket("view.plainSocket").instances()1
verifyEvalwindow.binding.socket("view.iteratedSocket").instances()4
getEvalwindow.model.data[0].push(2)
verifyEvalwindow.binding.socket("view.iteratedSocket").instances()5
verifyEvalwindow.binding.socket("view.plainSocket").instance(0).keys.length === 0true
verifyEvaltypeof window.binding.socket("view.plainSocket").instance(0).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(0).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(0).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(1).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(1).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(2).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(2).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(3).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(3).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(4).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(4).element !== "undefined"true
125 | 126 | 127 | -------------------------------------------------------------------------------- /test/selenium/14-unmount.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14-unmount 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
14-unmount
open${baseUrl}/test/selenium/14-unmount.html
assertTextcss=spanPlainSocket
assertTextcss=span.keys0 - 0
assertText//div[@id='template']/div[2]/div/div[2]/div/div/span1 - 0
assertText//div[@id='template']/div[2]/div[2]/div/div/div/span0 - 1
assertText//div[@id='template']/div[2]/div[2]/div[2]/div/div/span1 - 1
verifyEvalwindow.binding.socket("view.plainSocket").instances()1
verifyEvalwindow.binding.socket("view.iteratedSocket").instances()4
getEvalwindow.model.data[0].push(2)
verifyEvalwindow.binding.socket("view.iteratedSocket").instances()5
verifyEvalwindow.binding.socket("view.plainSocket").instance(0).keys.length === 0true
verifyEvaltypeof window.binding.socket("view.plainSocket").instance(0).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(0).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(0).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(1).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(1).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(2).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(2).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(3).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(3).element !== "undefined"true
verifyEvalwindow.binding.socket("view.iteratedSocket").instance(4).keys.length === 2true
verifyEvaltypeof window.binding.socket("view.iteratedSocket").instance(4).element !== "undefined"true
126 | 127 | 128 | -------------------------------------------------------------------------------- /src/core/util/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Ralf S. Engelschall 4 | ** Copyright (c) 2014 Johannes Rummel 5 | ** 6 | ** This Source Code Form is subject to the terms of the Mozilla Public 7 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 8 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | /* 12 | ** Utility function: create an exception string for throwing 13 | */ 14 | _api.util.exception = (msg) => { 15 | return new Error(msg) 16 | } 17 | 18 | /* 19 | ** Checks, if an object is an instance of Reference 20 | */ 21 | _api.util.isReference = (obj) => { 22 | return obj && obj instanceof _api.engine.binding.Reference 23 | } 24 | 25 | /* 26 | ** Generates a string that looks like a GUID and has similar randomness 27 | ** Adapted from: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 28 | */ 29 | _api.util.getGuid = (() => { 30 | function s4() { 31 | return Math.floor((1 + Math.random()) * 0x10000) 32 | .toString(16) 33 | .substring(1); 34 | } 35 | return () => { 36 | return s4() + s4() + "-" + s4() + "-" + s4() + "-" + 37 | s4() + "-" + s4() + s4() + s4(); 38 | } 39 | })() 40 | 41 | /* 42 | ** Checks, if value is primitive (boolean, number, string, undefined, null) 43 | */ 44 | _api.util.isPrimitive = (value) => { 45 | let type = typeof value 46 | return type === "boolean" || 47 | type === "number" || 48 | type === "string" || 49 | type === "undefined" || 50 | value === null 51 | } 52 | 53 | /* 54 | ** Returns the input or the underlying value, if value is a Reference 55 | */ 56 | _api.util.convertIfReference = (value) => { 57 | if (value instanceof _api.engine.binding.Reference) { 58 | return value.getValue() 59 | } else { 60 | return value 61 | } 62 | } 63 | 64 | /* 65 | ** Returns the same object with all References replaced by their values 66 | */ 67 | _api.util.convertToValues = (value) => { 68 | return _api.engine.binding.convertToValues(value) 69 | } 70 | 71 | /* 72 | ** Executes fn for each item of the array or for each key of the object 73 | */ 74 | _api.util.each = (array, fn) => { 75 | if (array) { 76 | let breaK = {} 77 | if (array instanceof Array) { 78 | for (var i = 0; i < array.length; i++) { 79 | let result = fn(array[i], i, array, breaK) 80 | if (result === breaK) { 81 | break 82 | } 83 | } 84 | } else if (typeof array === "object") { 85 | for (let key in array) { 86 | if (array.hasOwnProperty(key)) { 87 | let result = fn(array[key], key, array, breaK) 88 | if (result === breaK) { 89 | break 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | /* 98 | ** Recursively calls callback for each item of value, which is neither an object 99 | ** nor an array. Callback may return a value which replaces the old 100 | */ 101 | _api.util.traverseStructure = (value, callback) => { 102 | if (value instanceof Array) { 103 | let newArr = [] 104 | for (let i = 0; i < value.length; i++) { 105 | newArr.push(_api.util.traverseStructure(value[i], callback)) 106 | } 107 | return newArr 108 | } else if (typeof value === "object" && value.constructor.name === "Object") { 109 | let newObj = {} 110 | for (let key in value) { 111 | let newValue = _api.util.traverseStructure(value[key], callback) 112 | newObj[key] = newValue 113 | } 114 | return newObj 115 | } else { 116 | return callback(value) 117 | } 118 | } 119 | 120 | /* 121 | ** If condition is false, an (unspecific) exception is thrown 122 | */ 123 | _api.util.assume = (condition) => { 124 | if (!condition) { 125 | throw _api.util.exception("Internal assumption error") 126 | } 127 | } -------------------------------------------------------------------------------- /test/selenium/7-pause.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7-pause 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
7-pause
open${baseUrl}/test/selenium/2-iteration.html
getEvalwindow.binding.pause()
getEvalwindow.model.data[0][0].push("foo")
getEvalwindow.model.data[0][1].push("bar")
getEvalwindow.model.data[1][0].push("baz")
getEvalwindow.model.data[1][1].push("quux")
getEvalwindow.binding.resume()
assertText//div[@id='template']/div/div/div/div[3]/divfoo
verifyValuexpath=(//input[@type='text'])[3]foo
assertText//div[@id='template']/div/div/div[2]/div[3]/divbar
verifyValuexpath=(//input[@type='text'])[6]bar
assertText//div[@id='template']/div/div[2]/div/div[3]/divbaz
verifyValuexpath=(//input[@type='text'])[9]baz
assertText//div[@id='template']/div/div[2]/div[2]/div[3]/divquux
verifyValuexpath=(//input[@type='text'])[12]quux
getEvalwindow.binding.pause()
getEvalwindow.model.data[0][0][2] = "val0"
getEvalwindow.model.data[0][1][2] = "val1"
getEvalwindow.model.data[1][0][2] = "val2"
getEvalwindow.model.data[1][1][2] = "val3"
getEvalwindow.binding.resume()
assertText//div[@id='template']/div/div/div/div[3]/divval0
verifyValuexpath=(//input[@type='text'])[3]val0
assertText//div[@id='template']/div/div/div[2]/div[3]/divval1
verifyValuexpath=(//input[@type='text'])[6]val1
assertText//div[@id='template']/div/div[2]/div/div[3]/divval2
verifyValuexpath=(//input[@type='text'])[9]val2
assertText//div[@id='template']/div/div[2]/div[2]/div[3]/divval3
verifyValuexpath=(//input[@type='text'])[12]val3
161 | 162 | 163 | -------------------------------------------------------------------------------- /grunt/lint-2-eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "grunt": { 3 | "browser": false, 4 | "node": false, 5 | "amd": false 6 | }, 7 | 8 | "rules": { 9 | "no-alert": 2, 10 | "no-array-constructor": 2, 11 | "no-bitwise": 0, 12 | "no-caller": 0, 13 | "no-catch-shadow": 2, 14 | "no-comma-dangle": 2, 15 | "no-cond-assign": 2, 16 | "no-console": 2, 17 | "no-constant-condition": 2, 18 | "no-control-regex": 2, 19 | "no-debugger": 2, 20 | "no-delete-var": 2, 21 | "no-div-regex": 0, 22 | "no-dupe-keys": 2, 23 | "no-else-return": 0, 24 | "no-empty": 2, 25 | "no-empty-class": 2, 26 | "no-empty-label": 2, 27 | "no-eq-null": 0, 28 | "no-eval": 2, 29 | "no-ex-assign": 2, 30 | "no-extend-native": 2, 31 | "no-extra-boolean-cast": 2, 32 | "no-extra-parens": 0, 33 | "no-extra-semi": 2, 34 | "no-extra-strict": 2, 35 | "no-fallthrough": 2, 36 | "no-floating-decimal": 0, 37 | "no-func-assign": 2, 38 | "no-global-strict": 2, 39 | "no-implied-eval": 2, 40 | "no-inner-declarations": [2, "functions"], 41 | "no-invalid-regexp": 2, 42 | "no-iterator": 2, 43 | "no-label-var": 2, 44 | "no-labels": 2, 45 | "no-lone-blocks": 2, 46 | "no-lonely-if": 0, 47 | "no-loop-func": 0, 48 | "no-mixed-requires": [0, false], 49 | "no-multi-str": 2, 50 | "no-native-reassign": 2, 51 | "no-negated-in-lhs": 2, 52 | "no-nested-ternary": 0, 53 | "no-new": 2, 54 | "no-new-func": 2, 55 | "no-new-object": 2, 56 | "no-new-require": 0, 57 | "no-new-wrappers": 2, 58 | "no-obj-calls": 2, 59 | "no-octal": 2, 60 | "no-octal-escape": 2, 61 | "no-path-concat": 0, 62 | "no-plusplus": 0, 63 | "no-process-exit": 2, 64 | "no-proto": 2, 65 | "no-redeclare": 2, 66 | "no-regex-spaces": 2, 67 | "no-restricted-modules": 0, 68 | "no-return-assign": 2, 69 | "no-script-url": 2, 70 | "no-self-compare": 0, 71 | "no-sequences": 2, 72 | "no-shadow": 2, 73 | "no-shadow-restricted-names": 2, 74 | "no-spaced-func": 0, 75 | "no-space-before-semi": 2, 76 | "no-sparse-arrays": 2, 77 | "no-sync": 0, 78 | "no-ternary": 0, 79 | "no-trailing-spaces": 0, 80 | "no-undef": 2, 81 | "no-undefined": 0, 82 | "no-undef-init": 2, 83 | "no-underscore-dangle": 0, 84 | "no-unreachable": 2, 85 | "no-unused-expressions": 2, 86 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 87 | "no-use-before-define": 2, 88 | "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], 89 | "no-with": 2, 90 | "no-wrap-func": 2, 91 | "no-yoda": 2, 92 | 93 | "block-scoped-var": 0, 94 | "brace-style": [0, "1tbs"], 95 | "camelcase": 0, 96 | "complexity": [0, 11], 97 | "consistent-return": 2, 98 | "consistent-this": [0, "that"], 99 | "curly": [0, "all"], 100 | "default-case": 0, 101 | "dot-notation": 2, 102 | "eol-last": 0, 103 | "eqeqeq": 2, 104 | "func-names": 0, 105 | "func-style": [0, "declaration"], 106 | "guard-for-in": 0, 107 | "max-depth": [0, 4], 108 | "max-len": [0, 80, 4], 109 | "max-nested-callbacks": [0, 2], 110 | "max-params": [0, 3], 111 | "max-statements": [0, 10], 112 | "handle-callback-err": 0, 113 | "new-cap": 0, 114 | "new-parens": 2, 115 | "one-var": 0, 116 | "quote-props": 0, 117 | "quotes": [2, "double"], 118 | "radix": 0, 119 | "semi": 0, 120 | "sort-vars": 0, 121 | "space-after-keywords": [0, "always"], 122 | "space-in-brackets": [0, "never"], 123 | "space-infix-ops": 2, 124 | "space-return-throw-case": 2, 125 | "space-unary-word-ops": 0, 126 | "strict": 0, 127 | "use-isnan": 2, 128 | "valid-jsdoc": 0, 129 | "valid-typeof": 2, 130 | "wrap-iife": 0, 131 | "wrap-regex": 0 132 | } 133 | } -------------------------------------------------------------------------------- /test/selenium/1-binding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 34 | 84 | 85 | 86 |
87 |
88 |
89 |
90 |
91 | otv2m
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 100 |
101 | 102 | -------------------------------------------------------------------------------- /test/selenium/2-iteration-3.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 2-iteration-3 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 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 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 |
2-iteration-3
open${baseUrl}/test/selenium/2-iteration.html
getEvalwindow.model.data[0].push(window.array1)
getEvalwindow.model.data[1].push(window.array2)
assertText//div[@id='template']/div/div/div[3]/div/divval1
verifyValuexpath=(//input[@type='text'])[5]val1
assertText//div[@id='template']/div/div/div[3]/div[2]/divval2
verifyValuexpath=(//input[@type='text'])[6]val2
assertText//div[@id='template']/div/div[2]/div[3]/div/divval3
verifyValuexpath=(//input[@type='text'])[11]val3
assertText//div[@id='template']/div/div[2]/div[3]/div[2]/divval4
verifyValuexpath=(//input[@type='text'])[12]val4
typexpath=(//input[@type='text'])[5]foo
typexpath=(//input[@type='text'])[6]bar
typexpath=(//input[@type='text'])[11]baz
typexpath=(//input[@type='text'])[12]quux
assertText//div[@id='template']/div/div/div[3]/div/divfoo
assertText//div[@id='template']/div/div/div[3]/div[2]/divbar
assertText//div[@id='template']/div/div[2]/div[3]/div/divbaz
assertText//div[@id='template']/div/div[2]/div[3]/div[2]/divquux
verifyEvalwindow.model.data[0][2][0]foo
verifyEvalwindow.model.data[0][2][1]bar
verifyEvalwindow.model.data[1][2][0]baz
verifyEvalwindow.model.data[1][2][1]quux
getEvalwindow.model.data[0][2][0] = "val1"
getEvalwindow.model.data[0][2][1] = "val2"
getEvalwindow.model.data[1][2][0] = "val3"
getEvalwindow.model.data[1][2][1] = "val4"
assertText//div[@id='template']/div/div/div[3]/div/divval1
verifyValuexpath=(//input[@type='text'])[5]val1
assertText//div[@id='template']/div/div/div[3]/div[2]/divval2
verifyValuexpath=(//input[@type='text'])[6]val2
assertText//div[@id='template']/div/div[2]/div[3]/div/divval3
verifyValuexpath=(//input[@type='text'])[11]val3
assertText//div[@id='template']/div/div[2]/div[3]/div[2]/divval4
verifyValuexpath=(//input[@type='text'])[12]val4
190 | 191 | 192 | -------------------------------------------------------------------------------- /examples/Clock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | BindingJS - Clock 9 | 10 | 11 | 12 | 13 | 46 | 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 | -------------------------------------------------------------------------------- /src/core/adapter/on.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | $api.plugin("on", ($api, _api) => { 11 | class OnAdapter { 12 | 13 | constructor () { 14 | this.observer = new _api.util.Map() 15 | this.lastEvents = new _api.util.Map() 16 | this.observerCounter = new _api.util.Counter() 17 | } 18 | 19 | observe (element, path, params, callback) { 20 | if (path.length === 0) { 21 | throw _api.util.exception("The on adapter expects a qualifier") 22 | } 23 | let qualifier = path[0] 24 | let observerId = this.observerCounter.getNext() 25 | let keys = [] 26 | _api.util.each(params, (param) => { 27 | _api.util.array.addAll(keys, param.split(" ")) 28 | }) 29 | let keyCodes = [] 30 | _api.util.each(keys, (key) => { 31 | keyCodes.push(this.getKeyCode(key)) 32 | }) 33 | let handler = (event) => { 34 | if (keyCodes.length === 0 || _api.util.array.contains(keyCodes, event.keyCode)) { 35 | this.setLastEvent(element, qualifier, event) 36 | callback() 37 | } 38 | } 39 | element.on(qualifier, handler) 40 | this.observer.set(observerId, { handler: handler, element: element, qualifier: qualifier } ) 41 | return observerId 42 | } 43 | 44 | unobserve (observerId) { 45 | let observer = this.observer.get(observerId) 46 | if (observer) { 47 | observer.element.off(observer.qualifier, observer.handler) 48 | } 49 | } 50 | 51 | // Fires events if value is true 52 | set (element, path, value) { 53 | if (path.length === 0) { 54 | throw _api.util.exception("The on adapter expects a qualifier") 55 | } 56 | if (value) { 57 | switch (path[0]) { 58 | case "focus": element.focus(); break 59 | case "show": element.show(); break 60 | case "hide": element.hide(); break 61 | default: throw _api.util.exception("Could not interpret qualifier " + 62 | path[0] + " when setting on adapter") 63 | } 64 | } 65 | } 66 | 67 | getPaths (element, path) { 68 | return [path] 69 | } 70 | 71 | getValue (element, path) { 72 | if (path.length === 0) { 73 | throw _api.util.exception("The on adapter expects a qualifier") 74 | } 75 | return this.lastEvents.get(element).get(path[0]) 76 | } 77 | 78 | type () { 79 | return "view" 80 | } 81 | 82 | setLastEvent (element, qualifier, event) { 83 | if (!this.lastEvents.hasKey(element)) { 84 | this.lastEvents.set(element, new _api.util.Map()) 85 | } 86 | this.lastEvents.get(element).set(qualifier, event) 87 | } 88 | 89 | getKeyCode (key) { 90 | switch (key) { 91 | case "backspace": return 8 92 | case "tab": return 9 93 | case "enter": return 13 94 | case "return": return 13 95 | case "shift": return 16 96 | case "ctrl": return 17 97 | case "alt": return 18 98 | case "pause": return 19 99 | case "capslock": return 20 100 | case "escape": return 27 101 | case "esc": return 27 102 | case "pageup": return 33 103 | case "pagedown": return 34 104 | case "end": return 35 105 | case "home": return 36 106 | case "leftarrow": return 37 107 | case "uparrow": return 38 108 | case "rightarrow": return 39 109 | case "downarrow": return 40 110 | case "insert": return 45 111 | case "delete": return 46 112 | case "f1": return 112 113 | case "f2": return 113 114 | case "f3": return 114 115 | case "f4": return 115 116 | case "f5": return 116 117 | case "f6": return 117 118 | case "f7": return 118 119 | case "f8": return 119 120 | case "f9": return 120 121 | case "f10": return 121 122 | case "f11": return 122 123 | case "f12": return 123 124 | default: 125 | if (key.length === 1) { 126 | return key.charCodeAt(0) 127 | } else { 128 | throw _api.util.exception("Could not interpret key " + key) 129 | } 130 | } 131 | } 132 | } 133 | 134 | return new OnAdapter() 135 | }) 136 | -------------------------------------------------------------------------------- /test/selenium/13-expression.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 61 | 122 | 123 | 124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | 144 | -------------------------------------------------------------------------------- /src/core/preprocessor/preprocessor.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** BindingJS -- View Data Binding for JavaScript 3 | ** Copyright (c) 2014 Johannes Rummel 4 | ** 5 | ** This Source Code Form is subject to the terms of the Mozilla Public 6 | ** License (MPL), version 2.0. If a copy of the MPL was not distributed 7 | ** with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | _api.preprocessor.preprocess = (viewDataBinding) => { 11 | let template = viewDataBinding.vars.template 12 | let bind = viewDataBinding.vars.ast 13 | let bindingScopePrefix = viewDataBinding.bindingScopePrefix() 14 | let tempCounter = viewDataBinding.vars.tempCounter 15 | 16 | // Step 1: Check if iteration ids were used earlier and if they're all starting with bindingScopePrefix 17 | // This is a sanity check. Counter example: 18 | // ... { 19 | // @entry <- $foo 20 | // li (@entry, @key: $bar) { ... } 21 | // } 22 | _api.preprocessor.validate.checkIterationIds(bind, bindingScopePrefix) 23 | 24 | // Step 2: Rename sockets, so that they include their ancestor group names 25 | _api.preprocessor.transform.renameSockets(bind) 26 | 27 | // Step 3: Check, if all bindings only use one of the symbols <-, ->, <~, ~> or <-> 28 | _api.preprocessor.validate.checkDirections(bind) 29 | 30 | // Step 4: Transform two-way bindings into two one way bindings 31 | _api.preprocessor.convenience.twoWayBindings(bind) 32 | 33 | // Step 5: Check if Adapter were used inside parameters of source adapter and give a 34 | // warning if so 35 | _api.preprocessor.validate.checkSourceParameters(bind) 36 | 37 | // Step 6: Replace selectors with their elements in the template 38 | // Scopes might be duplicated because of 39 | // - Multiple matches for one selector 40 | // - CombinationLists (e.g. '.foo, #bar') 41 | // - Nested structures with CombinationLists which result in exponential many new scopes 42 | // e.g. '.foo, .bar { ... .baz, .quux {} }) evaluates the following selectors 43 | // against the template: 44 | // - .foo .baz 45 | // - .foo .quux 46 | // - .bar .baz 47 | // - .bar .quux 48 | // Assuming that each selector matches exactly one element, the original 49 | // is then replaced by four new scopes with these elements 50 | _api.preprocessor.transform.expandSelectors(template, bind) 51 | 52 | // Step 7: Check that one socket always exactly matches only one element 53 | // from the template 54 | _api.preprocessor.validate.checkSockets(bind) 55 | 56 | // Step 8: Make all references to the binding scope unique 57 | // This way we do not have to deal with scoping later (except if new items 58 | // in iterations are added 59 | // Example (Brackets are scopes and elements inside the brackets are ids): 60 | // (A) 61 | // / \ 62 | // (B) (A) 63 | // / \ 64 | // (A) (B) 65 | // ===> 66 | // (0) 67 | // / \ 68 | // (1) (0) 69 | // / \ 70 | // (0) (2) 71 | // All A's reference the same value since they have a common ancestor 72 | // The two B's however reference different values 73 | _api.preprocessor.transform.makeTempRefsUnique(bind, bindingScopePrefix, tempCounter) 74 | 75 | 76 | // Step 9: Make every iteration read out of the temp scope. 77 | // This makes it easier to implement the iteration since it has 78 | // to only observe the temp scope 79 | // Example 80 | // ... { 81 | // li (@temp: $collection) { ... } 82 | // } 83 | // ===> 84 | // ... { 85 | // @input <- $collection 86 | // li (@temp: @input) { ... } 87 | // } 88 | // 89 | // NOTE: It is never necessary to extract a two way binding 90 | // since @input is artificial and never written. The elements 91 | // inside however may be references or values, and a back-propagation 92 | // always happens through references and never through the binding 93 | _api.preprocessor.transform.extractIterationCollections(bind, bindingScopePrefix, tempCounter) 94 | 95 | // Step 10: Change parameters, so that each parameter is a single adapter without parameter 96 | // Example: @foo(@bar(@baz)) nests parameter, which is changed to the following set of bindings 97 | // @temp <- @bar(@baz) 98 | // .. @foo(@temp) ... 99 | _api.preprocessor.convenience.parameter(bind, bindingScopePrefix, tempCounter) 100 | 101 | // Step 11: Transform expressions 102 | _api.preprocessor.convenience.expression(bind, bindingScopePrefix, tempCounter) 103 | 104 | // Step 12: Prevent iterating over the same element more than once 105 | // This would lead to confusion and the order in which the binding is written would affect the template 106 | // It is however always possible to define the same element multiple times in the template 107 | _api.preprocessor.validate.preventMultiIteration(bind) 108 | 109 | // Step 13: Move Bindings that affect iterated elements into the iteration 110 | _api.preprocessor.transform.nestIteratedBindings(bind) 111 | 112 | // Step 14: Setup iteration tree 113 | let iterationTree = _api.preprocessor.iterator.setupIterationTree(bind, template) 114 | 115 | // Step 15: Mark the sockets in the iteration tree 116 | _api.preprocessor.transform.markSockets(iterationTree) 117 | 118 | // Step 16: Setup expanded iteration tree 119 | _api.preprocessor.iterator.setupExpandedIterationTree(viewDataBinding, iterationTree) 120 | 121 | viewDataBinding.vars.iterationTree = iterationTree 122 | viewDataBinding.vars.binding = bind 123 | delete viewDataBinding.vars.ast 124 | } 125 | -------------------------------------------------------------------------------- /test/selenium/example-TodoMVC.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | example-TodoMVC 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 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 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 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 | 242 |
example-TodoMVC
open${baseUrl}/examples/TodoMVC/index.html
sendKeyscss=#new-todofoo${KEY_ENTER}
sendKeyscss=#new-todobar${KEY_ENTER}
sendKeyscss=#new-todobaz${KEY_ENTER}
verifyEvalwindow.model.todos[0].completedfalse
verifyEvalwindow.model.todos[1].completedfalse
verifyEvalwindow.model.todos[2].completedfalse
verifyEvalwindow.model.todos[0].titlefoo
verifyEvalwindow.model.todos[1].titlebar
verifyEvalwindow.model.todos[2].titlebaz
clickxpath=(//input[@type='checkbox'])[3]
verifyEvalwindow.model.todos[1].completedtrue
clickxpath=(//input[@type='checkbox'])[3]
verifyEvalwindow.model.todos[1].completedfalse
getEvalwindow.model.todos[2].completed = true
assertCheckedxpath=(//input[@type='checkbox'])[4]
getEvalwindow.model.todos[2].completed = false
assertNotCheckedxpath=(//input[@type='checkbox'])[4]
click//ul[@id='todo-list']/li[2]/div/button
verifyEvalwindow.model.todos[0].completedfalse
verifyEvalwindow.model.todos[1].completedfalse
verifyEvalwindow.model.todos[0].titlefoo
verifyEvalwindow.model.todos[1].titlebaz
verifyEvalwindow.model.todos.length2
sendKeyscss=#new-todoquux${KEY_ENTER}
verifyEvalwindow.model.todos.length3
getEvalwindow.model.todos.splice(1, 1)
assertText//ul[@id='todo-list']/li[1]/div/labelfoo
assertText//ul[@id='todo-list']/li[2]/div/labelquux
getEvalwindow.model.todos.push({title: "qux", completed: false})
assertText//ul[@id='todo-list']/li[3]/div/labelqux
doubleClick//ul[@id='todo-list']/li[2]/div/label
typecss=li.editing>input.editbar
fireEventcss=li.editing>input.editblur
doubleClick//ul[@id='todo-list']/li[3]/div/label
typecss=li.editing>input.editbaz
fireEventcss=li.editing>input.editblur
verifyEvalwindow.model.todos[0].completedfalse
verifyEvalwindow.model.todos[1].completedfalse
verifyEvalwindow.model.todos[2].completedfalse
verifyEvalwindow.model.todos[0].titlefoo
verifyEvalwindow.model.todos[1].titlebar
verifyEvalwindow.model.todos[2].titlebaz
verifyEvalwindow.model.todos.length3
243 | 244 | 245 | -------------------------------------------------------------------------------- /test/selenium/2-iteration-2.testcase: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 2-iteration-2 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 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 | 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 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 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 |
2-iteration-2
open${baseUrl}/test/selenium/2-iteration.html
getEvalwindow.model.data[0][0].push("foo")
getEvalwindow.model.data[0][1].push("bar")
getEvalwindow.model.data[1][0].push("baz")
getEvalwindow.model.data[1][1].push("quux")
assertText//div[@id='template']/div/div/div/div[3]/divfoo
verifyValuexpath=(//input[@type='text'])[3]foo
assertText//div[@id='template']/div/div/div[2]/div[3]/divbar
verifyValuexpath=(//input[@type='text'])[6]bar
assertText//div[@id='template']/div/div[2]/div/div[3]/divbaz
verifyValuexpath=(//input[@type='text'])[9]baz
assertText//div[@id='template']/div/div[2]/div[2]/div[3]/divquux
verifyValuexpath=(//input[@type='text'])[12]quux
getEvalwindow.model.data[0][0][2] = "val0"
getEvalwindow.model.data[0][1][2] = "val1"
getEvalwindow.model.data[1][0][2] = "val2"
getEvalwindow.model.data[1][1][2] = "val3"
assertText//div[@id='template']/div/div/div/div[3]/divval0
verifyValuexpath=(//input[@type='text'])[3]val0
assertText//div[@id='template']/div/div/div[2]/div[3]/divval1
verifyValuexpath=(//input[@type='text'])[6]val1
assertText//div[@id='template']/div/div[2]/div/div[3]/divval2
verifyValuexpath=(//input[@type='text'])[9]val2
assertText//div[@id='template']/div/div[2]/div[2]/div[3]/divval3
verifyValuexpath=(//input[@type='text'])[12]val3
getEvalwindow.model.data[0][0].splice(2, 1)
getEvalwindow.model.data[0][1].splice(2, 1)
getEvalwindow.model.data[1][0].splice(2, 1)
getEvalwindow.model.data[1][1].splice(2, 1)
assertTextcss=div.fifth0
assertText//div[@id='template']/div/div/div/div[2]/div1
assertText//div[@id='template']/div/div/div[2]/div/div2
assertText//div[@id='template']/div/div/div[2]/div[2]/div3
assertText//div[@id='template']/div/div[2]/div/div/div4
assertText//div[@id='template']/div/div[2]/div/div[2]/div5
assertText//div[@id='template']/div/div[2]/div[2]/div/div6
assertText//div[@id='template']/div/div[2]/div[2]/div[2]/div7
verifyValuecss=input[type="text"]0
verifyValuexpath=(//input[@type='text'])[2]1
verifyValuexpath=(//input[@type='text'])[3]2
verifyValuexpath=(//input[@type='text'])[4]3
verifyValuexpath=(//input[@type='text'])[5]4
verifyValuexpath=(//input[@type='text'])[6]5
verifyValuexpath=(//input[@type='text'])[7]6
verifyValuexpath=(//input[@type='text'])[8]7
240 | 241 | 242 | --------------------------------------------------------------------------------