@qooxdoo/framework
Version:
The JS Framework for Coders
178 lines (156 loc) • 6.22 kB
JavaScript
/* ************************************************************************
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()
}
);
}
}
});