UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

178 lines (156 loc) 6.22 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2009 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Martin Wittemann (martinwittemann) ************************************************************************ */ /** * Mixin used for the bubbling events. If you want to use this in your own model * classes, be sure that every property will call the * {@link #_applyEventPropagation} function on every change. */ qx.Mixin.define("qx.data.marshal.MEventBubbling", { events : { /** * The change event which will be fired on every change in the model no * matter what property changes. This event bubbles so the root model will * fire a change event on every change of its children properties too. * * Note that properties are required to call * {@link #_applyEventPropagation} on apply for changes to be tracked as * desired. It is already taken care of that properties created with the * {@link qx.data.marshal.Json} marshaler call this method. * * The data will contain a map with the following four keys * <li>value: The new value of the property</li> * <li>old: The old value of the property.</li> * <li>name: The name of the property changed including its parent * properties separated by dots.</li> * <li>item: The item which has the changed property.</li> * Due to that, the <code>getOldData</code> method will always return null * because the old data is contained in the map. */ "changeBubble": "qx.event.type.Data" }, members : { /** * Apply function for every property created with the * {@link qx.data.marshal.Json} marshaler. It fires and * {@link #changeBubble} event on every change. It also adds the chaining * listener if possible which is necessary for the bubbling of the events. * * @param value {var} The new value of the property. * @param old {var} The old value of the property. * @param name {String} The name of the changed property. */ _applyEventPropagation : function(value, old, name) { this.fireDataEvent("changeBubble", { value: value, name: name, old: old, item: this }); this._registerEventChaining(value, old, name); }, /** * Registers for the given parameters the changeBubble listener, if * possible. It also removes the old listener, if an old item with * a changeBubble event is given. * * @param value {var} The new value of the property. * @param old {var} The old value of the property. * @param name {String} The name of the changed property. */ _registerEventChaining : function(value, old, name) { // if an old value is given, remove the old listener if possible if (old != null && old.getUserData && old.getUserData("idBubble-" + this.$$hash) != null) { var listeners = old.getUserData("idBubble-" + this.$$hash); for (var i = 0; i < listeners.length; i++) { old.removeListenerById(listeners[i]); } old.setUserData("idBubble-" + this.$$hash, null); } // if the child supports chaining if ((value instanceof qx.core.Object) && qx.Class.hasMixin(value.constructor, qx.data.marshal.MEventBubbling) ) { // create the listener var listener = qx.lang.Function.bind( this.__changePropertyListener, this, name ); // add the listener var id = value.addListener("changeBubble", listener, this); var listeners = value.getUserData("idBubble-" + this.$$hash); if (listeners == null) { listeners = []; value.setUserData("idBubble-" + this.$$hash, listeners); } listeners.push(id); } }, /** * Listener responsible for formating the name and firing the change event * for the changed property. * * @param name {String} The name of the former properties. * @param e {qx.event.type.Data} The date event fired by the property * change. */ __changePropertyListener : function(name, e) { var data = e.getData(); var value = data.value; var old = data.old; // if the target is an array if (qx.Class.hasInterface(e.getTarget().constructor, qx.data.IListData)) { if (data.name.indexOf) { var dotIndex = data.name.indexOf(".") != -1 ? data.name.indexOf(".") : data.name.length; var bracketIndex = data.name.indexOf("[") != -1 ? data.name.indexOf("[") : data.name.length; // brackets in the first spot is ok [BUG #5985] if (bracketIndex == 0) { var newName = name + data.name; } else if (dotIndex < bracketIndex) { var index = data.name.substring(0, dotIndex); var rest = data.name.substring(dotIndex + 1, data.name.length); if (rest[0] != "[") { rest = "." + rest; } var newName = name + "[" + index + "]" + rest; } else if (bracketIndex < dotIndex) { var index = data.name.substring(0, bracketIndex); var rest = data.name.substring(bracketIndex, data.name.length); var newName = name + "[" + index + "]" + rest; } else { var newName = name + "[" + data.name + "]"; } } else { var newName = name + "[" + data.name + "]"; } // if the target is not an array } else { // special case for array as first element of the chain [BUG #5985] if (parseInt(name) == name && name !== "") { name = "[" + name + "]"; } var newName = name + "." + data.name; } this.fireDataEvent( "changeBubble", { value: value, name: newName, old: old, item: data.item || e.getTarget() } ); } } });