From f42c0cb0aa15a8064d53184f1545dd53a5f21808 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 2 Apr 2015 15:37:53 +0200 Subject: [PATCH] Remove 2-way binding from objects --- examples_src/bind-input.html | 51 --- examples_src/bind-input.js | 69 ---- src/ol/control/overviewmapcontrol.js | 4 +- src/ol/dom/input.js | 149 --------- src/ol/object.js | 278 +--------------- test/spec/ol/object.test.js | 453 +-------------------------- 6 files changed, 17 insertions(+), 987 deletions(-) delete mode 100644 examples_src/bind-input.html delete mode 100644 examples_src/bind-input.js delete mode 100644 src/ol/dom/input.js diff --git a/examples_src/bind-input.html b/examples_src/bind-input.html deleted file mode 100644 index c707acf809..0000000000 --- a/examples_src/bind-input.html +++ /dev/null @@ -1,51 +0,0 @@ ---- -template: example.html -title: Bind HTML input example -shortdesc: Demonstrates two-way binding of HTML input elements to OpenLayers objects. -docs: > - - -tags: "custom, control" ---- -
-
-
-
-
-
-
-
- Layer - - - -
- -
-
-
-
-
- View - - - - -
-
-
diff --git a/examples_src/bind-input.js b/examples_src/bind-input.js deleted file mode 100644 index 5940453060..0000000000 --- a/examples_src/bind-input.js +++ /dev/null @@ -1,69 +0,0 @@ -goog.require('ol.Map'); -goog.require('ol.View'); -goog.require('ol.control'); -goog.require('ol.dom.Input'); -goog.require('ol.has'); -goog.require('ol.layer.Tile'); -goog.require('ol.source.OSM'); - - -function checkWebGL(evt) { - document.getElementById('no-webgl').style.display = - ol.has.WEBGL ? 'none' : ''; - document.getElementById('has-webgl').style.display = - ol.has.WEBGL && !evt.glContext ? '' : 'none'; - document.getElementById('webgl').style.display = - evt.glContext ? '' : 'none'; -} - -var layer = new ol.layer.Tile({ - source: new ol.source.OSM() -}); -layer.once('precompose', checkWebGL); - -var view = new ol.View({ - center: [0, 0], - zoom: 2 -}); - -var map = new ol.Map({ - layers: [layer], - renderer: exampleNS.getRendererFromQueryString(), - target: 'map', - controls: ol.control.defaults({ - attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ - collapsible: false - }) - }), - view: view -}); - -var visible = new ol.dom.Input(document.getElementById('visible')); -visible.bindTo('checked', layer, 'visible'); - -var opacity = new ol.dom.Input(document.getElementById('opacity')); -opacity.bindTo('value', layer, 'opacity') - .transform(parseFloat, String); - -var hue = new ol.dom.Input(document.getElementById('hue')); -hue.bindTo('value', layer, 'hue') - .transform(parseFloat, String); - -var saturation = new ol.dom.Input(document.getElementById('saturation')); -saturation.bindTo('value', layer, 'saturation') - .transform(parseFloat, String); - -var contrast = new ol.dom.Input(document.getElementById('contrast')); -contrast.bindTo('value', layer, 'contrast') - .transform(parseFloat, String); - -var brightness = new ol.dom.Input(document.getElementById('brightness')); -brightness.bindTo('value', layer, 'brightness') - .transform(parseFloat, String); - - -var rotation = new ol.dom.Input(document.getElementById('rotation')); -rotation.bindTo('value', view, 'rotation').transform(parseFloat, String); - -var resolution = new ol.dom.Input(document.getElementById('resolution')); -resolution.bindTo('value', view, 'resolution').transform(parseFloat, String); diff --git a/src/ol/control/overviewmapcontrol.js b/src/ol/control/overviewmapcontrol.js index 9123e6d8df..f09c70d849 100644 --- a/src/ol/control/overviewmapcontrol.js +++ b/src/ol/control/overviewmapcontrol.js @@ -170,7 +170,7 @@ ol.control.OverviewMap.prototype.setMap = function(map) { // if no layers were set for the overviewmap map, then bind with // those in the main map if (this.ovmap_.getLayers().getLength() === 0) { - this.ovmap_.bindTo(ol.MapProperty.LAYERGROUP, map); + this.ovmap_.setLayerGroup(map.getLayerGroup()); } // bind current map view, or any new one @@ -202,7 +202,7 @@ ol.control.OverviewMap.prototype.bindView_ = function() { // FIXME - the overviewmap view rotation currently follows the one used // by the main map view. We could support box rotation instead. The choice // between the 2 modes would be made in a single option - this.ovmap_.getView().bindTo(ol.ViewProperty.ROTATION, view); + // this.ovmap_.getView().bindTo(ol.ViewProperty.ROTATION, view); }; diff --git a/src/ol/dom/input.js b/src/ol/dom/input.js deleted file mode 100644 index 8ba3d31a64..0000000000 --- a/src/ol/dom/input.js +++ /dev/null @@ -1,149 +0,0 @@ -goog.provide('ol.dom.Input'); -goog.provide('ol.dom.InputProperty'); - -goog.require('goog.asserts'); -goog.require('goog.events'); -goog.require('goog.events.EventType'); -goog.require('ol.Object'); - - -/** - * @enum {string} - */ -ol.dom.InputProperty = { - VALUE: 'value', - CHECKED: 'checked' -}; - - - -/** - * @classdesc - * Helper class for binding HTML input to an {@link ol.Object}. - * - * Example: - * - * // bind a checkbox with id 'visible' to a layer's visibility - * var visible = new ol.dom.Input(document.getElementById('visible')); - * visible.bindTo('checked', layer, 'visible'); - * - * @constructor - * @extends {ol.Object} - * @param {Element} target Target element. - * @api - */ -ol.dom.Input = function(target) { - goog.base(this); - - /** - * @private - * @type {HTMLInputElement} - */ - this.target_ = /** @type {HTMLInputElement} */ (target); - - goog.events.listen(this.target_, - [goog.events.EventType.CHANGE, goog.events.EventType.INPUT], - this.handleInputChanged_, false, this); - - goog.events.listen(this, - ol.Object.getChangeEventType(ol.dom.InputProperty.VALUE), - this.handleValueChanged_, false, this); - goog.events.listen(this, - ol.Object.getChangeEventType(ol.dom.InputProperty.CHECKED), - this.handleCheckedChanged_, false, this); -}; -goog.inherits(ol.dom.Input, ol.Object); - - -/** - * If the input is a checkbox, return whether or not the checkbox is checked. - * @return {boolean|undefined} The checked state of the Input. - * @observable - * @api - */ -ol.dom.Input.prototype.getChecked = function() { - return /** @type {boolean} */ (this.get(ol.dom.InputProperty.CHECKED)); -}; -goog.exportProperty( - ol.dom.Input.prototype, - 'getChecked', - ol.dom.Input.prototype.getChecked); - - -/** - * Get the value of the input. - * @return {string|undefined} The value of the Input. - * @observable - * @api - */ -ol.dom.Input.prototype.getValue = function() { - return /** @type {string} */ (this.get(ol.dom.InputProperty.VALUE)); -}; -goog.exportProperty( - ol.dom.Input.prototype, - 'getValue', - ol.dom.Input.prototype.getValue); - - -/** - * Sets the value of the input. - * @param {string} value The value of the Input. - * @observable - * @api - */ -ol.dom.Input.prototype.setValue = function(value) { - this.set(ol.dom.InputProperty.VALUE, value); -}; -goog.exportProperty( - ol.dom.Input.prototype, - 'setValue', - ol.dom.Input.prototype.setValue); - - -/** - * Set whether or not a checkbox is checked. - * @param {boolean} checked The checked state of the Input. - * @observable - * @api - */ -ol.dom.Input.prototype.setChecked = function(checked) { - this.set(ol.dom.InputProperty.CHECKED, checked); -}; -goog.exportProperty( - ol.dom.Input.prototype, - 'setChecked', - ol.dom.Input.prototype.setChecked); - - -/** - * @param {goog.events.BrowserEvent} browserEvent Browser event. - * @private - */ -ol.dom.Input.prototype.handleInputChanged_ = function(browserEvent) { - goog.asserts.assert(browserEvent.currentTarget == this.target_, - 'currentTarget should be the same as this.target_'); - var target = this.target_; - if (target.type === 'checkbox' || target.type === 'radio') { - this.setChecked(target.checked); - } else { - this.setValue(target.value); - } -}; - - -/** - * @param {goog.events.Event} event Change event. - * @private - */ -ol.dom.Input.prototype.handleCheckedChanged_ = function(event) { - this.target_.checked = /** @type {boolean} */ (this.getChecked()); -}; - - -/** - * @param {goog.events.Event} event Change event. - * @private - */ -ol.dom.Input.prototype.handleValueChanged_ = function(event) { - this.target_.value = /** @type {string} */ (this.getValue()); -}; diff --git a/src/ol/object.js b/src/ol/object.js index 5dd3e0c369..9d64797c0f 100644 --- a/src/ol/object.js +++ b/src/ol/object.js @@ -10,9 +10,7 @@ goog.provide('ol.ObjectEventType'); goog.require('goog.events'); goog.require('goog.events.Event'); -goog.require('goog.functions'); goog.require('goog.object'); -goog.require('goog.string'); goog.require('ol.Observable'); @@ -64,63 +62,6 @@ goog.inherits(ol.ObjectEvent, goog.events.Event); -/** - * @constructor - * @param {ol.Object} source Source object. - * @param {ol.Object} target Target object. - * @param {string} sourceKey Source key. - * @param {string} targetKey Target key. - */ -ol.ObjectAccessor = function(source, target, sourceKey, targetKey) { - - /** - * @type {ol.Object} - */ - this.source = source; - - /** - * @type {ol.Object} - */ - this.target = target; - - /** - * @type {string} - */ - this.sourceKey = sourceKey; - - /** - * @type {string} - */ - this.targetKey = targetKey; - - /** - * @type {function(?): ?} - */ - this.from = goog.functions.identity; - - /** - * @type {function(?): ?} - */ - this.to = goog.functions.identity; -}; - - -/** - * @param {function(?): ?} from A function that transforms the source value - * before it is set to the target. - * @param {function(?): ?} to A function that transforms the target value - * before it is set to the source. - * @api - */ -ol.ObjectAccessor.prototype.transform = function(from, to) { - var oldValue = ol.Object.getKeyValue_(this.source, this.sourceKey); - this.from = from; - this.to = to; - this.source.notify(this.sourceKey, oldValue); -}; - - - /** * @classdesc * Abstract base class; normally only used for creating subclasses and not @@ -157,12 +98,6 @@ ol.ObjectAccessor.prototype.transform = function(from, to) { * first will be a `hasOwnProperty`; the second will appear in * `getProperties()`. Only the second is observable. * - * The observable properties also implement a form of Key Value Observing. - * Two objects can be bound together such that a change in one will - * automatically be reflected in the other. See `bindTo` method for more - * details, and see {@link ol.dom.Input} for the specific case of binding an - * object with an HTML element. - * * Properties can be deleted by using the unset method. E.g. * object.unset('foo'). * @@ -187,18 +122,6 @@ ol.Object = function(opt_values) { */ this.values_ = {}; - /** - * @private - * @type {Object.} - */ - this.accessors_ = {}; - - /** - * @private - * @type {Object.} - */ - this.listeners_ = {}; - if (goog.isDef(opt_values)) { this.setProperties(opt_values); } @@ -213,20 +136,6 @@ goog.inherits(ol.Object, ol.Observable); ol.Object.changeEventTypeCache_ = {}; -/** - * @private - * @type {Object.} - */ -ol.Object.getterNameCache_ = {}; - - -/** - * @private - * @type {Object.} - */ -ol.Object.setterNameCache_ = {}; - - /** * @param {string} key Key name. * @return {string} Change name. @@ -238,116 +147,6 @@ ol.Object.getChangeEventType = function(key) { }; -/** - * @param {string} key String. - * @return {string} Getter name. - */ -ol.Object.getGetterName = function(key) { - return ol.Object.getterNameCache_.hasOwnProperty(key) ? - ol.Object.getterNameCache_[key] : - (ol.Object.getterNameCache_[key] = 'get' + goog.string.capitalize(key)); -}; - - -/** - * @param {string} key String. - * @return {string} Setter name. - */ -ol.Object.getSetterName = function(key) { - return ol.Object.setterNameCache_.hasOwnProperty(key) ? - ol.Object.setterNameCache_[key] : - (ol.Object.setterNameCache_[key] = 'set' + goog.string.capitalize(key)); -}; - - -/** - * Get the value for an object and a key. Use the getter (`getX`) if it exists, - * otherwise use the generic `get` function. - * @param {ol.Object} obj Object. - * @param {string} key Key; - * @return {*} Value; - * @private - */ -ol.Object.getKeyValue_ = function(obj, key) { - var getterName = ol.Object.getGetterName(key); - var getter = /** @type {function(): *|undefined} */ - (/** @type {Object} */ (obj)[getterName]); - return goog.isDef(getter) ? getter.call(obj) : obj.get(key); -}; - - -/** - * Set the value for an object and a key. Use the setter (`setX`) if it exists, - * otherwise use the generic `set` function. - * @param {ol.Object} obj Object. - * @param {string} key Key. - * @param {*} value Value. - * @private - */ -ol.Object.setKeyValue_ = function(obj, key, value) { - var setterName = ol.Object.getSetterName(key); - var setter = /** @type {function(*)|undefined} */ - (/** @type {Object} */ (obj)[setterName]); - if (goog.isDef(setter)) { - setter.call(obj, value); - } else { - obj.set(key, value); - } -}; - - -/** - * The bindTo method allows you to set up a two-way binding between a - * `source` and `target` object. The method returns an object with a - * `transform` method that you can use to provide `from` and `to` - * functions to transform values on the way from the source to the - * target and on the way back. -* - * For example, if you had two map views (sourceView and targetView) - * and you wanted the target view to have double the resolution of the - * source view, you could transform the resolution on the way to and - * from the target with the following: - * - * sourceView.bindTo('resolution', targetView) - * .transform( - * function(sourceResolution) { - * // from sourceView.resolution to targetView.resolution - * return 2 * sourceResolution; - * }, - * function(targetResolution) { - * // from targetView.resolution to sourceView.resolution - * return targetResolution / 2; - * } - * ); - * - * @param {string} key Key name. - * @param {ol.Object} target Target. - * @param {string=} opt_targetKey Target key. - * @return {ol.ObjectAccessor} - * @api - */ -ol.Object.prototype.bindTo = function(key, target, opt_targetKey) { - var targetKey = opt_targetKey || key; - this.unbind(key); - - // listen for change:targetkey events - var eventType = ol.Object.getChangeEventType(targetKey); - this.listeners_[key] = goog.events.listen(target, eventType, - /** - * @param {ol.ObjectEvent} e Event. - * @this {ol.Object} - */ - function(e) { - this.notify(key, e.oldValue); - }, undefined, this); - - var accessor = new ol.ObjectAccessor(this, target, key, targetKey); - this.accessors_[key] = accessor; - this.notify(key, this.values_[key]); - return accessor; -}; - - /** * Gets a value. * @param {string} key Key name. @@ -356,12 +155,7 @@ ol.Object.prototype.bindTo = function(key, target, opt_targetKey) { */ ol.Object.prototype.get = function(key) { var value; - var accessors = this.accessors_; - if (accessors.hasOwnProperty(key)) { - var accessor = accessors[key]; - value = ol.Object.getKeyValue_(accessor.target, accessor.targetKey); - value = accessor.to(value); - } else if (this.values_.hasOwnProperty(key)) { + if (this.values_.hasOwnProperty(key)) { value = this.values_[key]; } return value; @@ -374,29 +168,7 @@ ol.Object.prototype.get = function(key) { * @api stable */ ol.Object.prototype.getKeys = function() { - var accessors = this.accessors_; - var keysObject; - if (goog.object.isEmpty(this.values_)) { - if (goog.object.isEmpty(accessors)) { - return []; - } else { - keysObject = accessors; - } - } else { - if (goog.object.isEmpty(accessors)) { - keysObject = this.values_; - } else { - keysObject = {}; - var key; - for (key in this.values_) { - keysObject[key] = true; - } - for (key in accessors) { - keysObject[key] = true; - } - } - } - return goog.object.getKeys(keysObject); + return goog.object.getKeys(this.values_); }; @@ -411,9 +183,6 @@ ol.Object.prototype.getProperties = function() { for (key in this.values_) { properties[key] = this.values_[key]; } - for (key in this.accessors_) { - properties[key] = this.get(key); - } return properties; }; @@ -438,16 +207,9 @@ ol.Object.prototype.notify = function(key, oldValue) { * @api stable */ ol.Object.prototype.set = function(key, value) { - var accessors = this.accessors_; - if (accessors.hasOwnProperty(key)) { - var accessor = accessors[key]; - value = accessor.from(value); - ol.Object.setKeyValue_(accessor.target, accessor.targetKey, value); - } else { - var oldValue = this.values_[key]; - this.values_[key] = value; - this.notify(key, oldValue); - } + var oldValue = this.values_[key]; + this.values_[key] = value; + this.notify(key, oldValue); }; @@ -465,36 +227,6 @@ ol.Object.prototype.setProperties = function(values) { }; -/** - * Removes a binding. Unbinding will set the unbound property to the current - * value. The object will not be notified, as the value has not changed. - * @param {string} key Key name. - * @api - */ -ol.Object.prototype.unbind = function(key) { - var listeners = this.listeners_; - var listener = listeners[key]; - if (listener) { - delete listeners[key]; - goog.events.unlistenByKey(listener); - var value = this.get(key); - delete this.accessors_[key]; - this.values_[key] = value; - } -}; - - -/** - * Removes all bindings. - * @api - */ -ol.Object.prototype.unbindAll = function() { - for (var key in this.listeners_) { - this.unbind(key); - } -}; - - /** * Unsets a property. * @param {string} key Key name. diff --git a/test/spec/ol/object.test.js b/test/spec/ol/object.test.js index 249df41a90..049ebea500 100644 --- a/test/spec/ol/object.test.js +++ b/test/spec/ol/object.test.js @@ -109,7 +109,7 @@ describe('ol.Object', function() { describe('notify', function() { - var listener1, listener2, listener3; + var listener1, listener2; beforeEach(function() { listener1 = sinon.spy(); @@ -117,11 +117,6 @@ describe('ol.Object', function() { listener2 = sinon.spy(); goog.events.listen(o, ol.ObjectEventType.PROPERTYCHANGE, listener2); - - var o2 = new ol.Object(); - o2.bindTo('k', o); - listener3 = sinon.spy(); - goog.events.listen(o2, 'change:k', listener3); }); it('dispatches events', function() { @@ -143,21 +138,11 @@ describe('ol.Object', function() { expect(event.key).to.be('k'); expect(event.oldValue).to.be(1); }); - - it('dispatches events to bound objects', function() { - o.notify('k', 1); - expect(listener3.calledOnce).to.be(true); - var args = listener3.firstCall.args; - expect(args).to.have.length(1); - var event = args[0]; - expect(event.key).to.be('k'); - expect(event.oldValue).to.be(1); - }); }); describe('set', function() { - var listener1, o2, listener2, listener3; + var listener1, listener2; beforeEach(function() { listener1 = sinon.spy(); @@ -165,11 +150,6 @@ describe('ol.Object', function() { listener2 = sinon.spy(); goog.events.listen(o, ol.ObjectEventType.PROPERTYCHANGE, listener2); - - o2 = new ol.Object(); - o2.bindTo('k', o); - listener3 = sinon.spy(); - goog.events.listen(o2, 'change:k', listener3); }); it('dispatches events to object', function() { @@ -177,7 +157,6 @@ describe('ol.Object', function() { expect(listener1).to.be.called(); expect(o.getKeys()).to.eql(['k']); - expect(o2.getKeys()).to.eql(['k']); }); it('dispatches generic change events to object', function() { @@ -189,259 +168,6 @@ describe('ol.Object', function() { expect(event.key).to.be('k'); }); - it('dispatches events to bound object', function() { - o.set('k', 1); - expect(listener3).to.be.called(); - }); - - it('dispatches events to object bound to', function() { - o2.set('k', 2); - expect(listener1).to.be.called(); - - expect(o.getKeys()).to.eql(['k']); - expect(o2.getKeys()).to.eql(['k']); - }); - - it('dispatches generic change events to object bound to', function() { - o2.set('k', 2); - expect(listener2.calledOnce).to.be(true); - var args = listener2.firstCall.args; - expect(args).to.have.length(1); - var event = args[0]; - expect(event.key).to.be('k'); - }); - - }); - - describe('bind', function() { - - var o2; - - beforeEach(function() { - o2 = new ol.Object(); - }); - - describe('bindTo after set', function() { - - beforeEach(function() { - o.set('k', 1); - o2.set('k', 0); - }); - - it('gets expected value', function() { - o2.bindTo('k', o); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - - expect(o.getKeys()).to.eql(['k']); - expect(o2.getKeys()).to.eql(['k']); - }); - - it('dispatches a change: event', function() { - var listener = sinon.spy(); - o2.on('change:k', listener); - o2.bindTo('k', o); - expect(listener.calledOnce).to.be(true); - var call = listener.firstCall; - expect(call.args).to.have.length(1); - expect(call.args[0].key).to.be('k'); - expect(call.args[0].oldValue).to.be(0); - expect(o2.get('k')).to.be(1); - }); - - it('dispatches a propertychange event', function() { - var listener = sinon.spy(); - o2.on('propertychange', listener); - o2.bindTo('k', o); - expect(listener.calledOnce).to.be(true); - var call = listener.firstCall; - expect(call.args).to.have.length(1); - expect(call.args[0].key).to.be('k'); - expect(call.args[0].oldValue).to.be(0); - expect(o2.get('k')).to.be(1); - }); - - }); - - describe('bindTo before set', function() { - - it('gets expected value', function() { - o2.bindTo('k', o); - o.set('k', 1); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - - expect(o.getKeys()).to.eql(['k']); - expect(o2.getKeys()).to.eql(['k']); - }); - }); - - describe('backwards', function() { - describe('bindTo after set', function() { - - it('gets expected value', function() { - o2.set('k', 1); - o2.bindTo('k', o); - expect(o.get('k')).to.be(undefined); - expect(o2.get('k')).to.be(undefined); - }); - }); - - describe('bindTo before set', function() { - - it('gets expected value', function() { - o2.bindTo('k', o); - o2.set('k', 1); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - }); - }); - }); - }); - - describe('unbind', function() { - var o2; - - beforeEach(function() { - o2 = new ol.Object(); - o2.bindTo('k', o); - o2.set('k', 1); - }); - - it('makes changes to unbound object invisible to other object', function() { - // initial state - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - o2.unbind('k'); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - o2.set('k', 2); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(2); - }); - - }); - - describe('unbindAll', function() { - var o2; - - beforeEach(function() { - o2 = new ol.Object(); - o2.bindTo('k', o); - o2.set('k', 1); - }); - - it('makes changes to unbound object invisible to other object', function() { - // initial state - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - o2.unbindAll(); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(1); - o2.set('k', 2); - expect(o.get('k')).to.eql(1); - expect(o2.get('k')).to.eql(2); - }); - }); - - describe('bind rename', function() { - var listener1, o2, listener2; - - beforeEach(function() { - o2 = new ol.Object(); - o2.bindTo('k2', o, 'k1'); - - listener1 = sinon.spy(); - goog.events.listen(o, 'change:k1', listener1); - - listener2 = sinon.spy(); - goog.events.listen(o2, 'change:k2', listener2); - }); - - it('sets the expected properties', function() { - o.set('k1', 1); - expect(o.get('k1')).to.eql(1); - expect(o.get('k2')).to.be(undefined); - expect(o2.get('k2')).to.eql(1); - expect(o2.get('k1')).to.be(undefined); - expect(listener1).to.be.called(); - expect(listener2).to.be.called(); - - expect(o.getKeys()).to.eql(['k1']); - expect(o2.getKeys()).to.eql(['k2']); - }); - }); - - describe('transitive bind', function() { - var o2, o3; - - beforeEach(function() { - o2 = new ol.Object(); - o3 = new ol.Object(); - o2.bindTo('k2', o, 'k1'); - o3.bindTo('k3', o2, 'k2'); - }); - - it('sets the expected properties', function() { - o.set('k1', 1); - expect(o.get('k1')).to.eql(1); - expect(o2.get('k2')).to.eql(1); - expect(o3.get('k3')).to.eql(1); - - expect(o.getKeys()).to.eql(['k1']); - expect(o2.getKeys()).to.eql(['k2']); - expect(o3.getKeys()).to.eql(['k3']); - }); - - describe('backward', function() { - - it('sets the expected properties', function() { - o3.set('k3', 1); - expect(o.get('k1')).to.eql(1); - expect(o2.get('k2')).to.eql(1); - expect(o3.get('k3')).to.eql(1); - - expect(o.getKeys()).to.eql(['k1']); - expect(o2.getKeys()).to.eql(['k2']); - expect(o3.getKeys()).to.eql(['k3']); - }); - }); - }); - - describe('circular bind', function() { - var o2; - - beforeEach(function() { - o2 = new ol.Object(); - o.bindTo('k', o2); - }); - - it('throws an error', function() { - expect(function() { o2.bindTo('k', o); }).to.throwException(); - }); - }); - - describe('priority', function() { - var o2; - - beforeEach(function() { - o2 = new ol.Object(); - }); - - it('respects set order', function() { - o.set('k', 1); - o2.set('k', 2); - o.bindTo('k', o2); - expect(o.get('k')).to.eql(2); - expect(o2.get('k')).to.eql(2); - }); - - it('respects set order (undefined)', function() { - o.set('k', 1); - o.bindTo('k', o2); - expect(o.get('k')).to.be(undefined); - expect(o2.get('k')).to.be(undefined); - }); }); describe('setter', function() { @@ -452,27 +178,12 @@ describe('ol.Object', function() { sinon.spy(o, 'setX'); }); - describe('without bind', function() { - it('does not call the setter', function() { - o.set('x', 1); - expect(o.get('x')).to.eql(1); - expect(o.setX).to.not.be.called(); + it('does not call the setter', function() { + o.set('x', 1); + expect(o.get('x')).to.eql(1); + expect(o.setX).to.not.be.called(); - expect(o.getKeys()).to.eql(['x']); - }); - }); - - describe('with bind', function() { - it('does call the setter', function() { - var o2 = new ol.Object(); - o2.bindTo('x', o); - o2.set('x', 1); - expect(o.setX).to.be.called(); - expect(o.get('x')).to.eql(1); - - expect(o.getKeys()).to.eql(['x']); - expect(o2.getKeys()).to.eql(['x']); - }); + expect(o.getKeys()).to.eql(['x']); }); }); @@ -484,29 +195,9 @@ describe('ol.Object', function() { sinon.spy(o, 'getX'); }); - describe('without bind', function() { - it('does not call the getter', function() { - expect(o.get('x')).to.be(undefined); - expect(o.getX).to.not.be.called(); - }); - }); - - describe('with bind', function() { - it('does call the getter', function() { - var o2 = new ol.Object(); - o2.bindTo('x', o); - expect(o2.get('x')).to.eql(1); - expect(o.getX).to.be.called(); - - expect(o.getKeys()).to.eql([]); - expect(o2.getKeys()).to.eql(['x']); - }); - }); - }); - - describe('bind self', function() { - it('throws an error', function() { - expect(function() { o.bindTo('k', o); }).to.throwException(); + it('does not call the getter', function() { + expect(o.get('x')).to.be(undefined); + expect(o.getX).to.not.be.called(); }); }); @@ -538,130 +229,6 @@ describe('ol.Object', function() { }); }); - describe('transforms', function() { - - describe('original states and events', function() { - it('bindTo and transform emit propertychange events', function() { - var source = new ol.Object(); - var target = new ol.Object(); - source.set('x', 1); - target.set('x', 2); - var sourceSpy = sinon.spy(); - var targetSpy = sinon.spy(); - source.on('propertychange', sourceSpy); - target.on('propertychange', targetSpy); - var accessor = source.bindTo('x', target); - expect(sourceSpy.callCount).to.be(1); - expect(targetSpy.callCount).to.be(0); - expect(source.get('x')).to.be(2); - expect(target.get('x')).to.be(2); - accessor.transform(function(v) { - return v * 2; - }, function(v) { - return v / 2; - }); - var call, args; - expect(sourceSpy.calledTwice).to.be(true); - call = sourceSpy.firstCall; - expect(call.args).to.have.length(1); - expect(call.args[0].key).to.be('x'); - expect(call.args[0].oldValue).to.be(1); - call = sourceSpy.secondCall; - expect(call.args).to.have.length(1); - expect(call.args[0].key).to.be('x'); - expect(call.args[0].oldValue).to.be(2); - expect(targetSpy.called).to.be(false); - expect(source.get('x')).to.be(1); - expect(target.get('x')).to.be(2); - }); - }); - - describe('with multiple binds to a single property', function() { - - var original, plusOne, asString; - - beforeEach(function() { - original = new ol.Object(); - original.set('x', 1); - plusOne = new ol.Object(); - plusOne.bindTo('x', original).transform( - function(x) { return x - 1; }, - function(x) { return x + 1; } - ); - asString = new ol.Object(); - asString.bindTo('x', original).transform( - function(x) { return +x; }, - function(x) { return x + ''; } - ); - }); - - it('returns the expected value', function() { - expect(original.get('x')).to.be(1); - expect(plusOne.get('x')).to.be(2); - expect(asString.get('x')).to.be('1'); - }); - - it('allows the original value to be set correctly', function() { - original.set('x', 2); - expect(plusOne.get('x')).to.be(3); - expect(asString.get('x')).to.be('2'); - }); - - it('allows the transformed values to be set correctly', function() { - plusOne.set('x', 3); - expect(original.get('x')).to.be(2); - expect(asString.get('x')).to.be('2'); - asString.set('x', '3'); - expect(original.get('x')).to.be(3); - expect(plusOne.get('x')).to.be(4); - }); - - }); - - describe('with transitive binds', function() { - - var original, plusOne, plusOneAsString; - - beforeEach(function() { - original = new ol.Object(); - original.set('x', 1); - plusOne = new ol.Object(); - plusOne.bindTo('x', original).transform( - function(x) { return x - 1; }, - function(x) { return x + 1; } - ); - plusOneAsString = new ol.Object(); - plusOneAsString.bindTo('x', plusOne).transform( - parseFloat, - function(x) { return x + ''; } - ); - }); - - it('returns the expected value', function() { - expect(original.get('x')).to.be(1); - expect(plusOne.get('x')).to.be(2); - expect(plusOneAsString.get('x')).to.be('2'); - }); - - it('allows the original value to be set correctly', function() { - original.set('x', 2); - expect(plusOne.get('x')).to.be(3); - expect(plusOneAsString.get('x')).to.be('3'); - }); - - it('allows the transformed values to be set correctly', function() { - plusOne.set('x', 3); - expect(original.get('x')).to.be(2); - expect(plusOneAsString.get('x')).to.be('3'); - plusOneAsString.set('x', '4'); - expect(original.get('x')).to.be(3); - expect(plusOne.get('x')).to.be(4); - }); - - }); - - }); - });