From ae7bfa6e545da1a0b632617248ace3f4a38ad900 Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Mon, 26 Mar 2012 18:10:36 -0400 Subject: [PATCH 1/5] Add `directiveArgument` class macro on view for defining nonstandard data- directives to make the values of keypaths available as properties on the view. --- lib/batman.js | 38 +++++++++++ src/batman.coffee | 17 ++++- tests/batman/test.html | 1 + tests/batman/view/view_subclass_test.coffee | 71 +++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/batman/view/view_subclass_test.coffee diff --git a/lib/batman.js b/lib/batman.js index fd93b54e..f81804f6 100644 --- a/lib/batman.js +++ b/lib/batman.js @@ -6699,6 +6699,28 @@ View.store = new Batman.ViewStore(); + View.directiveArgument = function() { + var keys; + var _this = this; + keys = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + keys.forEach(function(key) { + return _this.accessor(_this.prototype._argumentBindingKey(key), function(bindingKey) { + var context, keyPath, node, _ref; + if (!((node = this.get('node')) && (context = this.get('context')))) { + return; + } + keyPath = node.getAttribute(("data-" + key).toLowerCase()); + if (keyPath == null) return; + if ((_ref = this[bindingKey]) != null) _ref.die(); + return this[bindingKey] = new Batman.DOM.ViewArgumentBinding(node, keyPath, context); + }); + }); + return this.accessor.apply(this, __slice.call(keys).concat([function(key) { + var _ref; + return (_ref = this.get(this._argumentBindingKey(key))) != null ? _ref.get('filteredValue') : void 0; + }])); + }; + View.prototype.source = ''; View.prototype.html = ''; @@ -6761,6 +6783,10 @@ } }; + View.prototype._argumentBindingKey = function(key) { + return "_" + key + "ArgumentBinding"; + }; + View.prototype.on('appear', function() { return typeof this.viewDidAppear === "function" ? this.viewDidAppear.apply(this, arguments) : void 0; }); @@ -8835,6 +8861,18 @@ })(); + Batman.DOM.ViewArgumentBinding = (function() { + + __extends(ViewArgumentBinding, Batman.DOM.AbstractBinding); + + function ViewArgumentBinding() { + ViewArgumentBinding.__super__.constructor.apply(this, arguments); + } + + return ViewArgumentBinding; + + })(); + buntUndefined = function(f) { return function(value) { if (typeof value === 'undefined') { diff --git a/src/batman.coffee b/src/batman.coffee index 0f059ee4..6e09ba14 100644 --- a/src/batman.coffee +++ b/src/batman.coffee @@ -3983,6 +3983,17 @@ class Batman.View extends Batman.Object @observe 'node', (node) => @render(node) @store: new Batman.ViewStore() + @directiveArgument: (keys...) -> + keys.forEach (key) => + @accessor @::_argumentBindingKey(key), (bindingKey) -> + return unless (node = @get 'node') && (context = @get 'context') + keyPath = node.getAttribute "data-#{key}".toLowerCase() + return unless keyPath? + @[bindingKey]?.die() + @[bindingKey] = new Batman.DOM.ViewArgumentBinding node, keyPath, context + + @accessor keys..., (key) -> + @get(@_argumentBindingKey(key))?.get('filteredValue') # Set the source attribute to an html file to have that file loaded. source: '' @@ -4033,6 +4044,8 @@ class Batman.View extends Batman.Object @_renderer = new Batman.Renderer(node, null, @context, @) @_renderer.on 'rendered', => @fire('ready', node) + _argumentBindingKey: (key) -> "_#{key}ArgumentBinding" + @::on 'appear', -> @viewDidAppear? arguments... @::on 'disappear', -> @viewDidDisappear? arguments... @::on 'beforeAppear', -> @viewWillAppear? arguments... @@ -5446,10 +5459,12 @@ class Batman.DOM.IteratorBinding extends Batman.DOM.AbstractCollectionBinding @nodeMap.set(item, newNode) newNode +class Batman.DOM.ViewArgumentBinding extends Batman.DOM.AbstractBinding + # Filters # ------- # -# `Batman.Filters` contains the simple, determininistic tranforms used in view bindings to +# `Batman.Filters` contains the simple, deterministic transforms used in view bindings to # make life a little easier. buntUndefined = (f) -> (value) -> diff --git a/tests/batman/test.html b/tests/batman/test.html index ee68c5e7..a408b911 100644 --- a/tests/batman/test.html +++ b/tests/batman/test.html @@ -102,6 +102,7 @@ + diff --git a/tests/batman/view/view_subclass_test.coffee b/tests/batman/view/view_subclass_test.coffee new file mode 100644 index 00000000..840ac121 --- /dev/null +++ b/tests/batman/view/view_subclass_test.coffee @@ -0,0 +1,71 @@ +helpers = if typeof require is 'undefined' then window.viewHelpers else require './view_helper' + +QUnit.module "Batman.View subclasses: argument declaration and passing" + +test "should allow class level declaration of arguments", -> + class TestView extends Batman.View + @directiveArgument 'keyA', 'keyB', "notgiven" + + node = $('
')[0] + context = Batman one: "foo", two: "bar" + view = new TestView({node, context}) + equal view.get('keyA'), "foo" + equal view.get('keyB'), "bar" + equal view.get('notgiven'), undefined + +test "should allow keypaths as argument definitions", -> + class TestView extends Batman.View + @directiveArgument 'test' + + node = $('
')[0] + context = Batman + foo: Batman + bar: Batman + baz: "qux" + + view = new TestView({node, context}) + equal view.get('test'), "qux" + +test "should track keypath argument changes and update the property on the view", -> + class TestView extends Batman.View + @directiveArgument 'keyA', 'keyB' + + node = $('
')[0] + context = Batman one: "foo", two: "bar" + view = new TestView({node, context}) + equal view.get('keyA'), "foo" + equal view.get('keyB'), "bar" + context.set 'one', 10 + equal view.get('keyA'), 10 + equal view.get('keyB'), "bar" + +asyncTest "should make the arguments available in the context of the view", -> + class TestView extends Batman.View + @directiveArgument 'viewKey' + + source = '

' + context = Batman({TestView}) + + helpers.render source, context, (node) => + equal $('p', node).html(), "" + context.set "test", "foo" + equal $('p', node).html(), "foo" + context.set "test", "bar" + equal $('p', node).html(), "bar" + QUnit.start() + +test "should recreate argument bindings if the view's node changes", -> + class TestView extends Batman.View + @directiveArgument 'keyA', 'keyB' + + initalNode = $('
')[0] + newNode = $('
')[0] + + context = Batman one: "foo", two: "bar" + view = new TestView({node: initalNode, context}) + equal view.get('keyA'), "foo" + equal view.get('keyB'), "bar" + + view.set 'node', newNode + equal view.get('keyA'), "bar" + equal view.get('keyB'), "foo" From fbbbaebe31084d286fe09b1528c0d3406e0f7d13 Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Wed, 28 Mar 2012 11:42:12 -0400 Subject: [PATCH 2/5] Rename view argument binding class macro to directiveAccessor --- lib/batman.js | 2 +- src/batman.coffee | 2 +- tests/batman/view/view_subclass_test.coffee | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/batman.js b/lib/batman.js index f81804f6..0c3c5ef9 100644 --- a/lib/batman.js +++ b/lib/batman.js @@ -6699,7 +6699,7 @@ View.store = new Batman.ViewStore(); - View.directiveArgument = function() { + View.directiveAccessor = function() { var keys; var _this = this; keys = 1 <= arguments.length ? __slice.call(arguments, 0) : []; diff --git a/src/batman.coffee b/src/batman.coffee index 6e09ba14..99c360cb 100644 --- a/src/batman.coffee +++ b/src/batman.coffee @@ -3983,7 +3983,7 @@ class Batman.View extends Batman.Object @observe 'node', (node) => @render(node) @store: new Batman.ViewStore() - @directiveArgument: (keys...) -> + @directiveAccessor: (keys...) -> keys.forEach (key) => @accessor @::_argumentBindingKey(key), (bindingKey) -> return unless (node = @get 'node') && (context = @get 'context') diff --git a/tests/batman/view/view_subclass_test.coffee b/tests/batman/view/view_subclass_test.coffee index 840ac121..7a7d352e 100644 --- a/tests/batman/view/view_subclass_test.coffee +++ b/tests/batman/view/view_subclass_test.coffee @@ -4,7 +4,7 @@ QUnit.module "Batman.View subclasses: argument declaration and passing" test "should allow class level declaration of arguments", -> class TestView extends Batman.View - @directiveArgument 'keyA', 'keyB', "notgiven" + @directiveAccessor 'keyA', 'keyB', "notgiven" node = $('
')[0] context = Batman one: "foo", two: "bar" @@ -15,7 +15,7 @@ test "should allow class level declaration of arguments", -> test "should allow keypaths as argument definitions", -> class TestView extends Batman.View - @directiveArgument 'test' + @directiveAccessor 'test' node = $('
')[0] context = Batman @@ -28,7 +28,7 @@ test "should allow keypaths as argument definitions", -> test "should track keypath argument changes and update the property on the view", -> class TestView extends Batman.View - @directiveArgument 'keyA', 'keyB' + @directiveAccessor 'keyA', 'keyB' node = $('
')[0] context = Batman one: "foo", two: "bar" @@ -41,7 +41,7 @@ test "should track keypath argument changes and update the property on the view" asyncTest "should make the arguments available in the context of the view", -> class TestView extends Batman.View - @directiveArgument 'viewKey' + @directiveAccessor 'viewKey' source = '

' context = Batman({TestView}) @@ -56,7 +56,7 @@ asyncTest "should make the arguments available in the context of the view", -> test "should recreate argument bindings if the view's node changes", -> class TestView extends Batman.View - @directiveArgument 'keyA', 'keyB' + @directiveAccessor 'keyA', 'keyB' initalNode = $('
')[0] newNode = $('
')[0] From 15dacdae19e902759f21f398ca4e0bc4521046e0 Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Wed, 28 Mar 2012 16:52:59 -0400 Subject: [PATCH 3/5] Remove untested data-view-x bindings which are superseded by @directiveAccessor --- lib/batman.js | 9 --------- src/batman.coffee | 7 ------- 2 files changed, 16 deletions(-) diff --git a/lib/batman.js b/lib/batman.js index 0c3c5ef9..5a0eb29b 100644 --- a/lib/batman.js +++ b/lib/batman.js @@ -7245,15 +7245,6 @@ return typeof result === "object" ? result : child; })(Batman.DOM.FormBinding, arguments, function() {}); return context.descendWithKey(key, localName); - }, - view: function(node, bindKey, contextKey, context) { - var parent, view; - parent = context.contextForKey(contextKey); - view = null; - return parent.observeAndFire(contextKey, function(newValue) { - view || (view = Batman.data(node, 'view')); - return view != null ? view.set(bindKey, newValue) : void 0; - }); } }, events: { diff --git a/src/batman.coffee b/src/batman.coffee index 99c360cb..296b0b3e 100644 --- a/src/batman.coffee +++ b/src/batman.coffee @@ -4365,13 +4365,6 @@ Batman.DOM = { formfor: (node, localName, key, context) -> new Batman.DOM.FormBinding(arguments...) context.descendWithKey(key, localName) - - view: (node, bindKey, contextKey, context) -> - parent = context.contextForKey(contextKey) - view = null - parent.observeAndFire contextKey, (newValue) -> - view ||= Batman.data node, 'view' - view?.set bindKey, newValue } # `Batman.DOM.events` contains the helpers used for binding to events. These aren't called by From 3b03cd2bd9c807686cb10f77b078b542d889b2af Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Tue, 3 Apr 2012 14:14:52 -0400 Subject: [PATCH 4/5] Namespace view directive arguments under `data-view-` instead of just under `data-`. --- lib/batman.js | 2 +- src/batman.coffee | 2 +- tests/batman/view/view_subclass_test.coffee | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/batman.js b/lib/batman.js index 5a0eb29b..daf2ea8b 100644 --- a/lib/batman.js +++ b/lib/batman.js @@ -6709,7 +6709,7 @@ if (!((node = this.get('node')) && (context = this.get('context')))) { return; } - keyPath = node.getAttribute(("data-" + key).toLowerCase()); + keyPath = node.getAttribute(("data-view-" + key).toLowerCase()); if (keyPath == null) return; if ((_ref = this[bindingKey]) != null) _ref.die(); return this[bindingKey] = new Batman.DOM.ViewArgumentBinding(node, keyPath, context); diff --git a/src/batman.coffee b/src/batman.coffee index 296b0b3e..0c6f35a0 100644 --- a/src/batman.coffee +++ b/src/batman.coffee @@ -3987,7 +3987,7 @@ class Batman.View extends Batman.Object keys.forEach (key) => @accessor @::_argumentBindingKey(key), (bindingKey) -> return unless (node = @get 'node') && (context = @get 'context') - keyPath = node.getAttribute "data-#{key}".toLowerCase() + keyPath = node.getAttribute "data-view-#{key}".toLowerCase() return unless keyPath? @[bindingKey]?.die() @[bindingKey] = new Batman.DOM.ViewArgumentBinding node, keyPath, context diff --git a/tests/batman/view/view_subclass_test.coffee b/tests/batman/view/view_subclass_test.coffee index 7a7d352e..996222cc 100644 --- a/tests/batman/view/view_subclass_test.coffee +++ b/tests/batman/view/view_subclass_test.coffee @@ -6,7 +6,7 @@ test "should allow class level declaration of arguments", -> class TestView extends Batman.View @directiveAccessor 'keyA', 'keyB', "notgiven" - node = $('
')[0] + node = $('
')[0] context = Batman one: "foo", two: "bar" view = new TestView({node, context}) equal view.get('keyA'), "foo" @@ -17,7 +17,7 @@ test "should allow keypaths as argument definitions", -> class TestView extends Batman.View @directiveAccessor 'test' - node = $('
')[0] + node = $('
')[0] context = Batman foo: Batman bar: Batman @@ -30,7 +30,7 @@ test "should track keypath argument changes and update the property on the view" class TestView extends Batman.View @directiveAccessor 'keyA', 'keyB' - node = $('
')[0] + node = $('
')[0] context = Batman one: "foo", two: "bar" view = new TestView({node, context}) equal view.get('keyA'), "foo" @@ -43,7 +43,7 @@ asyncTest "should make the arguments available in the context of the view", -> class TestView extends Batman.View @directiveAccessor 'viewKey' - source = '

' + source = '

' context = Batman({TestView}) helpers.render source, context, (node) => @@ -58,8 +58,8 @@ test "should recreate argument bindings if the view's node changes", -> class TestView extends Batman.View @directiveAccessor 'keyA', 'keyB' - initalNode = $('
')[0] - newNode = $('
')[0] + initalNode = $('
')[0] + newNode = $('
')[0] context = Batman one: "foo", two: "bar" view = new TestView({node: initalNode, context}) From bf2349ca7a78ece4638bbe60475c35f5be7da4ed Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Tue, 3 Apr 2012 14:45:41 -0400 Subject: [PATCH 5/5] Rename `viewAccessor` to `option`. Horray! --- lib/batman.js | 2 +- src/batman.coffee | 2 +- tests/batman/view/view_subclass_test.coffee | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/batman.js b/lib/batman.js index daf2ea8b..2fa50ddf 100644 --- a/lib/batman.js +++ b/lib/batman.js @@ -6699,7 +6699,7 @@ View.store = new Batman.ViewStore(); - View.directiveAccessor = function() { + View.option = function() { var keys; var _this = this; keys = 1 <= arguments.length ? __slice.call(arguments, 0) : []; diff --git a/src/batman.coffee b/src/batman.coffee index 0c6f35a0..f8c11842 100644 --- a/src/batman.coffee +++ b/src/batman.coffee @@ -3983,7 +3983,7 @@ class Batman.View extends Batman.Object @observe 'node', (node) => @render(node) @store: new Batman.ViewStore() - @directiveAccessor: (keys...) -> + @option: (keys...) -> keys.forEach (key) => @accessor @::_argumentBindingKey(key), (bindingKey) -> return unless (node = @get 'node') && (context = @get 'context') diff --git a/tests/batman/view/view_subclass_test.coffee b/tests/batman/view/view_subclass_test.coffee index 996222cc..4dec206b 100644 --- a/tests/batman/view/view_subclass_test.coffee +++ b/tests/batman/view/view_subclass_test.coffee @@ -4,7 +4,7 @@ QUnit.module "Batman.View subclasses: argument declaration and passing" test "should allow class level declaration of arguments", -> class TestView extends Batman.View - @directiveAccessor 'keyA', 'keyB', "notgiven" + @option 'keyA', 'keyB', "notgiven" node = $('
')[0] context = Batman one: "foo", two: "bar" @@ -15,7 +15,7 @@ test "should allow class level declaration of arguments", -> test "should allow keypaths as argument definitions", -> class TestView extends Batman.View - @directiveAccessor 'test' + @option 'test' node = $('
')[0] context = Batman @@ -28,7 +28,7 @@ test "should allow keypaths as argument definitions", -> test "should track keypath argument changes and update the property on the view", -> class TestView extends Batman.View - @directiveAccessor 'keyA', 'keyB' + @option 'keyA', 'keyB' node = $('
')[0] context = Batman one: "foo", two: "bar" @@ -41,7 +41,7 @@ test "should track keypath argument changes and update the property on the view" asyncTest "should make the arguments available in the context of the view", -> class TestView extends Batman.View - @directiveAccessor 'viewKey' + @option 'viewKey' source = '

' context = Batman({TestView}) @@ -56,7 +56,7 @@ asyncTest "should make the arguments available in the context of the view", -> test "should recreate argument bindings if the view's node changes", -> class TestView extends Batman.View - @directiveAccessor 'keyA', 'keyB' + @option 'keyA', 'keyB' initalNode = $('
')[0] newNode = $('
')[0]