[KSS-checkins] r43176 - in kukit/kukit.js/trunk: 3rd_party kukit

reebalazs at codespeak.net reebalazs at codespeak.net
Fri May 11 13:01:15 CEST 2007


Author: reebalazs
Date: Fri May 11 13:01:14 2007
New Revision: 43176

Added:
   kukit/kukit.js/trunk/3rd_party/base2-dom-fp.js
      - copied unchanged from r43174, kukit/kukit.js/branch/performance_improvement/3rd_party/base2-dom-fp.js
Modified:
   kukit/kukit.js/trunk/kukit/dom.js
   kukit/kukit.js/trunk/kukit/eventreg.js
   kukit/kukit.js/trunk/kukit/oper.js
   kukit/kukit.js/trunk/kukit/plugin.js
   kukit/kukit.js/trunk/kukit/utils.js
Log:
Merge in performance_improvement brahch, -r41967:43168

Modified: kukit/kukit.js/trunk/kukit/dom.js
==============================================================================
--- kukit/kukit.js/trunk/kukit/dom.js	(original)
+++ kukit/kukit.js/trunk/kukit/dom.js	Fri May 11 13:01:14 2007
@@ -1,9 +1,6 @@
 /*
-* Copyright (c) 2005-2006
-* Authors:
-*   Godefroid Chapelle <gotcha at bubblenet.be>
-*   Florian Schulze <florian.schulze at gmx.net>
-*   Balázs Reé <ree at greenfinity.hu>
+* Copyright (c) 2005-2007
+* Authors: KSS Project Contributors (see docs/CREDITS.txt)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
@@ -123,18 +120,54 @@
 *  really the query should start from the document root, but
 *  limited to in_nodes subtrees!
 */
+
+
 kukit.dom.cssQuery = function(selector, in_nodes) {
     // to eliminate possible errors
     if (typeof(in_nodes) != 'undefined' && in_nodes == null) {
         throw 'Selection error in kukit.dom.cssQuery';
     }
+    return kukit.dom._cssQuery(selector, in_nodes);
+};
+
+/*
+ * Decide which query to use
+ */
+
+kukit.dom._cssQuery = function(selector, in_nodes) {
+    var USE_BASE2 = (typeof(base2) != 'undefined');
+    if (USE_BASE2) {
+        kukit.log('Using cssQuery from base2');
+        kukit.dom._cssQuery = kukit.dom._cssQuery_base2
+    } else {
+        kukit.log('Using original cssQuery');
+        kukit.dom._cssQuery = kukit.dom._cssQuery_orig
+    }
+    return kukit.dom._cssQuery(selector, in_nodes);
+};
+
+kukit.dom._cssQuery_base2 = function(selector, in_nodes) {
     // global scope, always.
-    var result = cssQuery(selector);
     // This is very bad. However the binding makes sure that
     // nodes once bound will never be bound again
     // (also, noticed the following issue: cssQuery, when called
     // on an element, does not check the element itself.)
-    return result;
+    var results = base2.DOM.Document.matchAll(document, selector);
+    var nodes = [];
+    for(var i = 0; i < results.length; i++) {
+        nodes.push(results.item(i));
+    }
+    return nodes;
+};
+
+kukit.dom._cssQuery_orig = function(selector, in_nodes) {
+    // global scope, always.
+    // This is very bad. However the binding makes sure that
+    // nodes once bound will never be bound again
+    // (also, noticed the following issue: cssQuery, when called
+    // on an element, does not check the element itself.)
+    var results = cssQuery(selector);
+    return results;
 };
 
 kukit.dom.focus = function(node) {
@@ -307,12 +340,12 @@
 *
 *  Scheduler for embedded window content loaded
 */
-kukit.dom.EmbeddedContentLoadedScheduler = function(framename, func) {
+kukit.dom.EmbeddedContentLoadedScheduler = function(framename, func, autodetect) {
     this.framename = framename;
     this.func = func;
+    this.autodetect = autodetect;
     var self = this;
     var f = function() {
-        kukit.logDebug('Is iframe loaded ?');
         self.check();
     };
     this.counter = new kukit.ut.TimerCounter(250, f, true);
@@ -337,35 +370,50 @@
 };
 
 kukit.dom.EmbeddedContentLoadedScheduler.prototype.check = function() {
-    // quit if the init function has already been called
+    
+    kukit.logDebug('Is iframe loaded ?');
+    
     var doc = kukit.dom.getIframeDocument(this.framename);
+
+    // quit if the init function has already been called
+    // XXX I believe we want to call the function too, then
     if (doc._embeddedContentLoadedInitDone) {
-        return;
+        kukit.logWarning('Iframe already initialized, but we execute the action enyway, as requested.');
+        this.counter.restart = false;
     }
 
-    // obviously we are not there... this happens on FF
-
-    /*Commented out by GC
-    if (doc.location.href == 'about:blank') {
-        return;
-    }*/
-    
+    // autodetect=false implements a more reliable detection method
+    // that involves cooperation from the internal document. In this
+    // case the internal document sets the _kssReadyForLoadEvent attribute
+    // on the document, when loaded. It is safe to check for this in any 
+    // case, however if this option is selected, we rely only on this, 
+    // and skip the otherwise problematic default checking.
     if (typeof doc._kssReadyForLoadEvent != 'undefined') {
         this.counter.restart = false;
     } 
-    // First check for Safari or
-    //if DOM methods are supported, and the body element exists
-    //(using a double-check including document.body, for the benefit of older moz builds [eg ns7.1] 
-    //in which getElementsByTagName('body')[0] is undefined, unless this script is in the body section)
-    
-    /*Commented out by GC
-    if(/KHTML|WebKit/i.test(navigator.userAgent)) {
-        if(/loaded|complete/.test(doc.readyState)) {
+
+    if (this.autodetect && this.counter.restart) {
+
+        // obviously we are not there... this happens on FF
+        if (doc.location.href == 'about:blank') {
+            return;
+        } /* */
+        
+        // First check for Safari or
+        //if DOM methods are supported, and the body element exists
+        //(using a double-check including document.body, for the benefit of older moz builds [eg ns7.1] 
+        //in which getElementsByTagName('body')[0] is undefined, unless this script is in the body section)
+        
+        if(/KHTML|WebKit/i.test(navigator.userAgent)) {
+            if(/loaded|complete/.test(doc.readyState)) {
+                this.counter.restart = false;
+            }
+        } else if(typeof doc.getElementsByTagName != 'undefined' && (doc.getElementsByTagName('body')[0] != null || doc.body != null)) {
             this.counter.restart = false;
-        }
-    } else if(typeof doc.getElementsByTagName != 'undefined' && (doc.getElementsByTagName('body')[0] != null || doc.body != null)) {
-        this.counter.restart = false;
-    }*/
+        } /* */
+
+    }
+
     if ( ! this.counter.restart) {
         kukit.logDebug('Yes, iframe is loaded.');
         doc._embeddedContentLoadedInitDone = true;

Modified: kukit/kukit.js/trunk/kukit/eventreg.js
==============================================================================
--- kukit/kukit.js/trunk/kukit/eventreg.js	(original)
+++ kukit/kukit.js/trunk/kukit/eventreg.js	Fri May 11 13:01:14 2007
@@ -1,9 +1,6 @@
 /*
-* Copyright (c) 2005-2006
-* Authors:
-*   Godefroid Chapelle <gotcha at bubblenet.be>
-*   Florian Schulze <florian.schulze at gmx.net>
-*   Balázs Reé <ree at greenfinity.hu>
+* Copyright (c) 2005-2007
+* Authors: KSS Project Contributors (see docs/CREDITS.txt)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
@@ -81,7 +78,7 @@
 /* events (methods) registration  helpers (not to be called directly) */
 
 kukit.er.EventRegistry.prototype._register = function(namespace, eventname, klass,
-        bindmethodname, defaultactionmethodname, bindmethodapi) {
+        bindmethodname, defaultactionmethodname, itername) {
     if (typeof(defaultactionmethodname) == 'undefined') {
         throw 'some arguments are not passed when calling EventRegistry.register';
     }
@@ -105,42 +102,55 @@
         }
         throw 'In EventRegistry.register double registration of key "' + key + '"';
     }
+    // check bindmethodname and defaultactionmethodname
+    if (bindmethodname && ! klass.prototype[bindmethodname])
+        throw 'In EventRegistry.register bind method "' + bindmethodname + '" is undefined for event "' + eventname + '" namespace "' + namespace + '"';
+    if (defaultactionmethodname && ! klass.prototype[defaultactionmethodname])
+        throw 'In EventRegistry.register default action method "' + defaultactionmethodname + '" is undefined for event "' + eventname + '" namespace "' + namespace + '"';
+    // check the iterator.
+    if  (! kukit.er.getBindIterator(itername)) {
+        throw 'In EventRegistry.register unknown bind iterator "' + itername + '"';
+    }
     // register it
     this.content[key] = {
         'classname': classname,
         'bindmethodname': bindmethodname,
         'defaultactionmethodname': defaultactionmethodname,
-        'bindmethodapi': bindmethodapi
+        'itername': itername
         };
 };
 
 /* events (methods) binding "ForAll" registration */
 
-kukit.er.EventRegistry.prototype._registerEventSet = function(namespace, names) {
-    // At this name the class and event should be checked already. so this should
+kukit.er.EventRegistry.prototype._registerEventSet = function(namespace, names, itername, bindmethodname) {
+    // At this name the values should be checked already. so this should
     // be called _after_ _register.
-    this.eventsets.push({'namespace': namespace, 'names': names});
+    this.eventsets.push({
+        'namespace': namespace, 
+        'names': names,
+        'itername': itername,
+        'bindmethodname': bindmethodname
+        });
 };
 
-
 /* there are the actual registration methods, to be called from plugins */
 
 kukit.er.EventRegistry.prototype.register = function(namespace, eventname, klass,
         bindmethodname, defaultactionmethodname) {
-    this._register(namespace, eventname, klass, bindmethodname, defaultactionmethodname, 'old');
-    this._registerEventSet(namespace, [eventname]);
+    this._register(namespace, eventname, klass, bindmethodname, defaultactionmethodname, 'each_legacy');
+    this._registerEventSet(namespace, [eventname], 'each_legacy', bindmethodname);
 };
 
 kukit.er.EventRegistry.prototype.registerForAllEvents = function(namespace, eventnames, klass,
-        bindmethodname, defaultactionmethodname) {
+        bindmethodname, defaultactionmethodname, itername) {
     if (typeof(eventnames) == 'string') {
         eventnames = [eventnames];
         }
     for (var i=0; i<eventnames.length; i++) {
         var eventname = eventnames[i];
-        this._register(namespace, eventname, klass, bindmethodname, defaultactionmethodname, 'new');
+        this._register(namespace, eventname, klass, bindmethodname, defaultactionmethodname, itername);
     }
-    this._registerEventSet(namespace, eventnames);
+    this._registerEventSet(namespace, eventnames, itername, bindmethodname);
 };
 
 kukit.er.EventRegistry.prototype._getKey = function(namespace, eventname) {
@@ -502,7 +512,7 @@
     // We mark a given oper. This means a binding on the binderinstance 
     // for given event, node and eventrule (containing event namespace,
     // name, and evt- parms.)
-
+    //
     // first see if it can go to already bound ones
     this.bound.checkOperBindable(oper);
     // then register it properly to the binding events
@@ -512,70 +522,13 @@
 kukit.er.BinderInfo.prototype.processBindingEvents = function () {
     // We came to the end of the binding phase. Now we process all our binding
     // events, This will do the actual binding on the browser side.
-    var eventRegistry = kukit.eventsGlobalRegistry;
-    for (var i=0; i < eventRegistry.eventsets.length; i++) {
-        var eventset = eventRegistry.eventsets[i];
-        if (this.binderinstance.__event_namespace__ == eventset.namespace) {
-            this._processBindingEventSet(eventset.names);
-        }
-    }
+    this.binding.processBindingEvents(this.binderinstance)
     // Now we to add these to the new ones.
     this.binding.propagateTo(this.bound);
     // Delete them from the registry, to protect against accidents.
     this.binding = null;
 };
 
-kukit.er.BinderInfo.prototype._processBindingEventSet = function (names) {
-    // Bind finally for all the opers collected
-    var opers = this.binding.getBoundOpersForEventSet(names);
-    if (opers.length == 0) {
-        return;
-    }
-    // find the bind method
-    // (We use the name and namespace from the first oper, as the bindmethod
-    // should be identical anyway.
-    var kss_selector = opers[0].eventrule.kss_selector;
-    var namespace = kss_selector.namespace;
-    var name = kss_selector.name;
-    var reg = kukit.eventsGlobalRegistry.get(namespace, name);
-    var methodname = reg.bindmethodname;
-    // XXX this is now disabled. We want to allow these events to "bind" on different nodes,
-    // however there is no actual event bound. 
-    if (! methodname) {
-        return;
-        //throw new kukit.err.rd.EventBindError('Method is not defined as bindable', name, namespace);
-    }
-    var method = this.binderinstance[methodname];
-    if (typeof(method) == 'undefined' ) {
-        throw new kukit.err.rd.EventBindError('Method "' + methodname + '" does not exist', name, namespace);
-    }
-    // Ok. Now decide if we go with the new or the old api.
-    if (reg.bindmethodapi == 'new') {
-        // Protect the binding for better logging
-        try {
-            method.call(binderinstance, opers);
-        } catch(e) {
-            throw new kukit.err.rd.EventBindError('Error during binding, reason: [' + e + ']', name, namespace);
-        }
-    } else { // old
-        for (var i=0; i<opers.length; i++) {
-            var oper = opers[i];
-            var func_to_bind = oper.makeExecuteActionsHook();
-            if (this.binderinstance != oper.binderinstance) {
-                throw new kukit.err.rd.EventBindError('fatal: wrong binder instance');
-            }
-            var binderinstance = oper.binderinstance;
-            var eventname = oper.getEventName();
-            // Protect the binding for better logging
-            try {
-                method.call(binderinstance, eventname, func_to_bind, oper);
-            } catch(e) {
-                throw new kukit.err.rd.EventBindError('Error during binding, reason: [' + e + ']', eventname, oper.getEventNamespace());
-            }
-        }
-    }
-};
-
 
 /*
 *  class OperRegistry
@@ -588,12 +541,14 @@
 */
 
 kukit.er.OperRegistry = function () {
-    this.info = {};
+    this.infopername = {};
+    this.infopernode = {};
 };
 
+// XXX XXX XXX we can do this without full cloning, more efficiently.
 kukit.er.OperRegistry.prototype.propagateTo = function (newreg) {
-    for (var key in this.info) {
-        var rules_per_name = this.info[key];
+    for (var key in this.infopername) {
+        var rules_per_name = this.infopername[key];
         for (var name in rules_per_name) {
             var oper = rules_per_name[name];
             newreg.bindOper(oper);
@@ -601,12 +556,18 @@
     }
 };
 
-kukit.er.OperRegistry.prototype.checkOperBindable = function (oper) {
+kukit.er.OperRegistry.prototype.checkOperBindable = function (oper, name, nodehash) {
     // Check if the binding with this oper could be done.
     // Throw exception otherwise.
-    var info = this.info;
-    var name = oper.eventrule.kss_selector.name;
-    var nodehash = kukit.rd.hashnode(oper.node);
+    //
+    // Remark. We need  different check and bind method, because we need to bind to the currently
+    // processed nodes, but we need to check duplication in all the previously bound nodes.
+    var info = this.infopername;
+    // name and nodehash are for speedup.
+    if (typeof(nodehash) == 'undefined') {
+        name = oper.eventrule.kss_selector.name;
+        nodehash = kukit.rd.hashnode(oper.node);
+    }
     var rules_per_name = info[name];
     if (typeof(rules_per_name) == 'undefined') {
         // Create an empty list.
@@ -619,15 +580,43 @@
     
 kukit.er.OperRegistry.prototype.bindOper = function (oper) {
     // Marks binding between binderinstance, eventname, node.
+    var name = oper.eventrule.kss_selector.name;
     var nodehash = kukit.rd.hashnode(oper.node);
-    var rules_per_name = this.checkOperBindable(oper);
+    var rules_per_name = this.checkOperBindable(oper, name, nodehash);
     rules_per_name[nodehash] = oper;
+    // also store per node info
+    var rules_per_node = this.infopernode[nodehash];
+    if (typeof(rules_per_node) == 'undefined') {
+        // Create an empty list.
+        rules_per_node = this.infopernode[nodehash] = {};
+    }
+    rules_per_node[name] = oper;
 };
 
+// XXX This will need refactoring.
+/// We would only want to lookup from our registry and not the other way around.
+kukit.er.OperRegistry.prototype.processBindingEvents = function (binderinstance) {
+    var eventRegistry = kukit.eventsGlobalRegistry;
+    for (var i=0; i < eventRegistry.eventsets.length; i++) {
+        var eventset = eventRegistry.eventsets[i];
+        // Only process binding events (and ignore non-binding ones)
+        if (eventset.bindmethodname) {
+            if (binderinstance.__event_namespace__ == eventset.namespace) {
+                // Process the binding event set. This will call the actual bindmethods
+                // according to the specified iterator.
+                var iterator = kukit.er.getBindIterator(eventset.itername);
+                iterator.call(this, eventset, binderinstance);
+            }
+        }
+    }
+};
+
+// XXX The following methods will probably disappear as iterators replace their functionality.
+
 kukit.er.OperRegistry.prototype.getBoundOperForNode = function (name, node) {
     // Get the oper that is bound to a given eventname to a node in this binderinstance
     // returns null, if there is no such oper.
-    var rules_per_name = this.info[name];
+    var rules_per_name = this.infopername[name];
     if (typeof(rules_per_name) == 'undefined') {
         return null;
     }
@@ -643,7 +632,7 @@
 kukit.er.OperRegistry.prototype.getBoundOpers = function (name) {
     // Get the opers bound to a given eventname (to any node) in this binderinstance
     var opers = [];
-    var rules_per_name = this.info[name];
+    var rules_per_name = this.infopername[name];
     if (typeof(rules_per_name) != 'undefined') {
         // take the values as a list
         for (var nodehash in rules_per_name) {
@@ -654,12 +643,94 @@
     return opers;
 };
 
-kukit.er.OperRegistry.prototype.getBoundOpersForEventSet = function (names) {
-    // Returns all opers for a given eventset.
+// Iterators
+// The getBindIterator returns a function that gets executed on
+// the oper registry.
+//
+// Iterators receive the eventset as a parameter
+// plus a binderinstance and a method. They need to iterate by calling this
+// as method.call(binderinstance, ...); where ... can be any parameters this
+// given iteration specifies.
+//
+
+kukit.er.getBindIterator = function(itername) {
+    return kukit.er.OperRegistry.prototype['iter_' + itername];
+};
+
+kukit.er.OperRegistry.prototype.call_bind_method = function (eventset, binderinstance, p1, p2, p3, p4, p5, p6) {
+    var method = binderinstance[eventset.bindmethodname];
+    // Protect the binding for better logging
+    try {
+        method.call(binderinstance, p1, p2, p3, p4, p5, p6);
+    } catch(e) {
+        throw new kukit.err.rd.EventBindError('Error during binding, reason: [' + e + ']',  eventset.names, eventset.namespace);
+    }
+};
+
+// This calls the bind method by each bound oper one by one. Eventname and func_to_bind are passed too.
+// this is the legacy ("each_legacy") way
+kukit.er.OperRegistry.prototype.iter_each_legacy = function (eventset, binderinstance) {
+    for (var i=0; i<eventset.names.length; i++) {
+        var rules_per_name = this.infopername[eventset.names[i]];
+        if (typeof(rules_per_name) != 'undefined') {
+            for (var nodehash in rules_per_name) {
+                var oper = rules_per_name[nodehash];
+                var eventname = oper.getEventName();
+                var func_to_bind = oper.makeExecuteActionsHook();
+                this.call_bind_method(eventset, binderinstance, eventname, func_to_bind, oper);
+            }
+        }
+    }
+};
+
+
+// This calls the bind method by each bound oper one by one. Eventname and func_to_bind are passed too.
+// this is the preferred ("each") way. Parameters are different from each_legacy.
+kukit.er.OperRegistry.prototype.iter_each = function (eventset, binderinstance) {
+    for (var i=0; i<eventset.names.length; i++) {
+        var rules_per_name = this.infopername[eventset.names[i]];
+        if (typeof(rules_per_name) != 'undefined') {
+            for (var nodehash in rules_per_name) {
+                var oper = rules_per_name[nodehash];
+                this.call_bind_method(eventset, binderinstance, oper);
+            }
+        }
+    }
+};
+
+// This calls the bind method by the list of bound opers
+kukit.er.OperRegistry.prototype.iter_opers = function (eventset, binderinstance) {
     var opers = [];
-    for (var i=0; i<names.length; i++) {
-        var name = names[i];
-        opers = opers.concat(this.getBoundOpers(name));
+    for (var i=0; i<eventset.names.length; i++) {
+        var rules_per_name = this.infopername[eventset.names[i]];
+        if (typeof(rules_per_name) != 'undefined') {
+            for (var nodehash in rules_per_name) {
+                opers.push(rules_per_name[nodehash]);
+            }
+        }
     }
-    return opers;
+    this.call_bind_method(eventset, binderinstance, opers);
 };
+
+// This calls the bind method by a mapping eventname:oper per each bound node individually
+kukit.er.OperRegistry.prototype.iter_node = function (eventset, binderinstance) {
+    for (var nodehash in this.infopernode) {
+        var rules_per_node = this.infopernode[nodehash];
+        // filter only the events we are interested in
+        var filtered_rules = {};
+        var found = false;
+        for (var i=0; i<eventset.names.length; i++) {
+            var name = eventset.names[i];
+            var oper = rules_per_node[name];
+            if (typeof(oper) != 'undefined') {
+                filtered_rules[name] = oper;
+                found = true;
+            }
+        }
+        // call it
+        if (found) {
+            this.call_bind_method(eventset, binderinstance, filtered_rules);
+        }
+    }
+};
+

Modified: kukit/kukit.js/trunk/kukit/oper.js
==============================================================================
--- kukit/kukit.js/trunk/kukit/oper.js	(original)
+++ kukit/kukit.js/trunk/kukit/oper.js	Fri May 11 13:01:14 2007
@@ -199,14 +199,26 @@
     return this.eventrule.kss_selector.namespace;
 };
 
-kukit.op.Oper.prototype.makeExecuteActionsHook = function () {
+kukit.op.Oper.prototype.makeExecuteActionsHook = function (filter) {
     // Factory that creates the function that executes the actions.
     // The function may take a dict that is updated on the oper 
+    // If filter is specified, it will we called with a function and
+    // the event will only be triggered if the filter returned true.
+    // THe return value of func_to_bind will show if the event
+    // has executed or not.
     var eventname = this.getEventName();
     var self = this;
     var func_to_bind = function(dict) {
+        // (XXX XXX TODO it should happen here, that we change to a different
+        // oper class. This is for the future when we separate the BindOper
+        // from the ActionOper.)
         var newoper = self.clone(dict, true);
+        // call the filter and if it says skip it, we are done
+        if (filter && ! filter(newoper)) return false;
+        // execute the event's actions
         newoper.binderinstance._EventBinder_triggerevent(eventname, newoper);
+        // show that the event's actions have been executed
+        return true;
     };
     return func_to_bind;
 };

Modified: kukit/kukit.js/trunk/kukit/plugin.js
==============================================================================
--- kukit/kukit.js/trunk/kukit/plugin.js	(original)
+++ kukit/kukit.js/trunk/kukit/plugin.js	Fri May 11 13:01:14 2007
@@ -1,9 +1,6 @@
 /*
-* Copyright (c) 2005-2006
-* Authors:
-*   Godefroid Chapelle <gotcha at bubblenet.be>
-*   Florian Schulze <florian.schulze at gmx.net>
-*   Balázs Reé <ree at greenfinity.hu>
+* Copyright (c) 2005-2007
+* Authors: KSS Project Contributors (see docs/CREDITS.txt)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
@@ -59,6 +56,77 @@
 };
 
 /*
+* function registerBrowserEvent
+*
+* This can be used to register native events in a way that
+* they handle allowbubbling, preventdefault and preventbubbling as needed.
+* (THe handling of these parms are optional, it is allowed not to have them
+* in the oper.parms.)
+*
+* THe register function can also take a filter function as parameter. 
+* This function needs to receive oper as a parameter,
+* where 'browserevent' will be set on oper too as the native browser event.
+* The function must return true if it wants the event to execure, false otherwise.
+* If it returns false, the event will not be prevented and counts as if
+* were not called.
+* This allows for certain event binder like key handlers, to put an extra condition
+* on the triggering of event.
+*
+* The eventname parameter is entirely optional and can be used to set up a different
+* event from the desired one.
+*/
+
+kukit.pl.registerBrowserEvent = function(oper, filter, eventname) {
+    var func_to_bind = oper.makeExecuteActionsHook(filter);
+    var func = function(e) {
+        target = kukit.pl.getTargetForBrowserEvent(e);
+        if (oper.parms.allowbubbling || target == oper.node) {
+            // Execute the action, provide browserevent on oper
+            // ... however, do it protected. We want the preventdefault
+            // in any case!
+            var exc;
+            var success;
+            try {
+                success = func_to_bind({'browserevent': e});
+            } catch(exc1) {
+                exc = exc1;    
+            }
+            if (success || exc) {
+                // This should only be skipped, if the filter told
+                // us that we don't need this event to be executed.
+                // If an exception happened during the event execution,
+                // we do yet want to proceed with the prevents.
+                //
+                // Cancel default event ?
+                if (oper.parms.preventdefault) {
+                    // W3C style
+                    if (e.preventDefault)
+                        e.preventDefault();
+                    // MS style
+                    try { e.returnValue = false; } catch (exc2) {}
+                }
+                // Prevent bubbling to other kss events ?
+                if (oper.parms.preventbubbling) {
+                    if (!e) var e = window.event;
+                    e.cancelBubble = true;
+                    if (e.stopPropagation) e.stopPropagation();
+                }
+            }
+            //
+            if (exc != null) {
+                // throw the original exception
+                throw exc;
+            }
+        } else {
+            kukit.log('Ignored bubbling event for "' + name + '" (target =' + target.tagName + '), EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid); 
+        }
+    };
+    if (! eventname)
+        eventname = oper.getEventName();
+    kukit.ut.registerEventListener(oper.node, eventname, func);
+};
+
+/*
 * class NativeEventBinder
 */
 kukit.pl.NativeEventBinder = function() {
@@ -103,103 +171,50 @@
             throw 'In native events only the click event can have preventdefault.';
         }
     }
-    var allowbubbling = oper.parms.allowbubbling;
-    var preventdefault = oper.parms.preventdefault;
-    var node = oper.node;
-    var func = function(e) {
-        target = kukit.pl.getTargetForBrowserEvent(e);
-        if (allowbubbling || target == node) {
-            
-            // Execute the action, provide browserevent on oper
-            // ... however, do it protected. We want the preventdefault
-            // in any case!
-            var exc;
-            try {
-                func_to_bind({'browserevent': e});
-            } catch(exc1) {
-                exc = exc1;    
-            }
-            // Cancel default event ?
-            if (preventdefault) {
-                // W3C style
-                if (e.preventDefault)
-                    e.preventDefault();
-                // MS style
-                try { e.returnValue = false; } catch (exc2) {}
-            }
-            // Prevent bubbling to other kss events ?
-            if (oper.parms.preventbubbling) {
-                if (!e) var e = window.event;
-	        e.cancelBubble = true;
-	        if (e.stopPropagation) e.stopPropagation();
-            }
-            //
-            if (exc != null) {
-                // throw the original exception
-                throw exc;
-            }
-        } else {
-            kukit.log('Ignored bubbling event for "' + name + '" (target =' + target.tagName + '), EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid); 
-        }
-    };
-    kukit.ut.registerEventListener(node, name, func);
+    kukit.pl.registerBrowserEvent(oper);
+    //
     // XXX Safari hack
     // necessary since Safari does not prevent the <a href...> following
     // (in case of allowbubbling we have to apply it to all clicks, as there
     // might be a link inside that we cannot detect on the current node)
-    if (preventdefault && kukit.HAVE_SAFARI 
-            && (allowbubbling || name == 'click' && node.tagName.toLowerCase() == 'a')) {
-        function cancelClickSafari() {
+    //
+    // XXX not needed since we have the legacy name parameter:
+    // var name = oper.getEventName();
+    if (oper.parms.preventdefault && kukit.HAVE_SAFARI 
+            && (oper.parms.allowbubbling || name == 'click' && oper.node.tagName.toLowerCase() == 'a')) {
+        oper.node.onclick = function cancelClickSafari() {
             return false;
-        }
-        node.onclick = cancelClickSafari;
+        };
     }
 };
 
 kukit.pl.NativeEventBinder.prototype.__bind_key__ = function(name, func_to_bind, oper) {
-    oper.completeParms([], {'preventdefault': 'true', 'allowbubbling': '', 'keycodes': ''},  'native key event binding');
+    oper.completeParms([], {'preventdefault': 'true', 'allowbubbling': '', 'preventbubbling': '', 'keycodes': ''},  'native key event binding');
     oper.evalList('keycodes', 'native key event binding');
     oper.evalBool('preventdefault', 'native key event binding');
     oper.evalBool('allowbubbling', 'native key event binding');
-    var allowbubbling = oper.parms.allowbubbling;
-    var preventdefault = oper.parms.preventdefault;
-    var node = oper.node;
-    // Convert keyCode to dict
-    var keycodes = {};
-    for (var i=0; i<oper.parms.keycodes.length; i++) {
-        keyCode = oper.parms.keycodes[i];
-        keycodes[keyCode] = true;
-    }
-    var func = function(e) {
-        target = kukit.pl.getTargetForBrowserEvent(e);
-        if (allowbubbling || target == node) {
-            var keyCode = e.keyCode.toString();
-            if (oper.parms.keycodes.length == 0 || keycodes[keyCode]) {         
-                // Execute the action, provide browserevent on oper
-                func_to_bind({'browserevent': e});
-                // Cancel default event
-                if (preventdefault) {
-                    // W3C style
-                    if (e.preventDefault)
-                        e.preventDefault();
-                    // MS style
-                    try { e.returnValue = false; } catch (exc) {}
-                }
-            } else {
-                kukit.log('Ignored event for "' + name + '", keycode ' + e.keyCode + ' not in ' + oper.parms.keycodes);
-            }
-        } else {
-            kukit.log('Ignored bubbling event for "' + name + '" (target =' + target.tagName + '), EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid); 
+    var filter;
+    if (oper.parms.keycodes.length >= 0) {
+        // Convert keyCode to dict
+        var keycodes = {};
+        for (var i=0; i<oper.parms.keycodes.length; i++) {
+            keyCode = oper.parms.keycodes[i];
+            keycodes[keyCode] = true;
         }
-    };
-    kukit.ut.registerEventListener(node, name, func);
+        // Set filter so that only the specified keys should trigger.
+        filter = function(oper) {
+            var keyCode = oper.browserevent.keyCode.toString();
+            return keycodes[keyCode];
+        };
+    }
+    kukit.pl.registerBrowserEvent(oper, filter);
 };
 
 /*
 *  Registration of all the native events that can bound to a node or to document 
 *  (= document or window, depending on the event specs)
 *  Unsupported are those with absolute no hope to work in a cross browser way
-*  Preventdefault is only allowed for click, currently
+*  Preventdefault is only allowed for click and key events, currently
 */
 kukit.eventsGlobalRegistry.register(null, 'blur', kukit.pl.NativeEventBinder, '__bind__nodeorwindow', null);
 kukit.eventsGlobalRegistry.register(null, 'focus', kukit.pl.NativeEventBinder, '__bind__nodeorwindow', null);
@@ -271,14 +286,21 @@
 kukit.pl.LoadEventBinder = function() {
 };
 
-kukit.pl.LoadEventBinder.prototype.__bind__ = function(name, func_to_bind, oper) {
-    oper.completeParms([], {'initial': 'true', 'insert': 'true'}, 'load event binding');
-    oper.evalBool('initial', 'load event binding');
-    oper.evalBool('insert', 'load event binding');
-    // Bind the function to the onload event of the node.
-    // If this is an iframe node, we have the possibility
-    // to wait until the execution totally finishes, so we load the real load event.
-    // node._kukitmark contains the binding phase.
+kukit.pl.LoadEventBinder.prototype.process_parms = function(oper, iload) {
+    if (! oper) {
+        return;
+    }
+    if (iload) {
+        oper.completeParms(['autodetect'], {'initial': 'true', 'insert': 'true'}, 'iload event binding');
+        // autodetect=false changes the iload autosense method to one that requires the iframe set
+        // the _kssReadyForLoadEvent attribute on the document. Setting this attribute is explicitely required 
+        // if autodetect is off, since otherwise in this case we would never notice if the document has arrived.
+        oper.evalBool('autodetect', 'iload event binding');
+    } else {
+        oper.completeParms([], {'initial': 'true', 'insert': 'true'}, 'load event binding');
+    }
+    oper.evalBool('initial', 'load/iload event binding');
+    oper.evalBool('insert', 'load/iload event binding');
     var phase = oper.node._kukitmark;
     if (phase == 1 && ! oper.parms.initial) {
         kukit.logDebug('EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid + ' event ignored, oninitial=false.');
@@ -288,23 +310,78 @@
         kukit.logDebug('EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid + ' event ignored, oninsert=false.');
         return;
     }
-    if (oper.node != null && oper.node.tagName.toLowerCase() == 'iframe' &&
-              (phase == 2 || (phase == 1 && kukit.engine.initializedOnDOMLoad))) {
-        kukit.logDebug('EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid + ' event selected delayed execution (when iframe loaded)');
-        // We want the event execute once the iframe is loaded.
-        var f = function() {
-            kukit.engine.bindScheduler.addPost(func_to_bind, 'Execute load event for iframe ' + oper.node.name);
-        };
-        new kukit.dom.EmbeddedContentLoadedScheduler(oper.node.id, f);
+    return oper;
+};
 
+kukit.pl.LoadEventBinder.prototype.__bind__ = function(opers_by_eventname) {
+    // This bind method handles load and iload events together, and opers_by_eventname is
+    // a dictionary of opers which can contain a load and an iload key, either one or both.
+    var loadoper = opers_by_eventname.load;
+    var iloadoper = opers_by_eventname.iload;
+    loadoper = this.process_parms(loadoper);
+    iloadoper = this.process_parms(iloadoper, true);
+    var anyoper = loadoper || iloadoper;
+    if (! anyoper) {
+        return;
+    }
+    if (anyoper.node != null && anyoper.node.tagName.toLowerCase() == 'iframe') {
+        // In an iframe.
+        //
+        // BBB If there is only a load (and no iload) event bound to this node, 
+        // we interpret it as an iload event, but issue deprecation warning.
+        // This conserves legacy behaviour when the load event was actually doing an iload,
+        // when it was bound to an iframe node.
+        // The deprecation tells that the event should be changed from load to iload.
+        if (loadoper && ! iloadoper) {
+            iloadoper = loadoper;
+            loadoper = null;
+            // with the legacy loads we suppose autodetect=false
+            iloadoper.parms.autodetect = false;
+            kukit.logWarning('Deprecated the use of "load" event for iframes. It will behave differently in the future. Use the "iload" event (maybe with evt-iload-autodetect: false) instead!');
+        } 
     } else {
-        kukit.logDebug('EventRule #' + oper.eventrule.getNr() + ' mergeid ' + oper.eventrule.kss_selector.mergeid + ' event selected normal postponed execution.');
+        // Not an iframe. So iload is not usable.
+        if (iloadoper) {
+            throw 'iload event can only be bound on an iframe node.';
+        }
+    }
+    // Now, bind the events.
+    if (loadoper) {
+        kukit.logDebug('EventRule #' + loadoper.eventrule.getNr() + ' mergeid ' + loadoper.eventrule.kss_selector.mergeid + ' event selected normal postponed execution.');
         // for any other node than iframe, or even for iframe in phase1, we need to execute immediately.
-        kukit.engine.bindScheduler.addPost(func_to_bind, 'Execute load event for node ' + oper.node.tagName.toLowerCase());
+        var func_to_bind = loadoper.makeExecuteActionsHook();
+        kukit.engine.bindScheduler.addPost(func_to_bind, 'Execute load event for node ' + loadoper.node.tagName.toLowerCase());
+    }
+    if (iloadoper) {
+        var phase = iloadoper.node._kukitmark;
+        // For phase 2 we need to execute posponed, for phase1 immediately.
+        // XXX it would be better not need this and do always postponed.
+        if (phase == 2 || (phase == 1 && kukit.engine.initializedOnDOMLoad)) {
+            kukit.logDebug('EventRule #' + iloadoper.eventrule.getNr() + ' mergeid ' + iloadoper.eventrule.kss_selector.mergeid + ' event selected delayed execution (when iframe loaded)');
+            // We want the event execute once the iframe is loaded.
+            // In a somewhat tricky way, we start the scheduler only from the normal delayed execution. This will enable that in
+            // case we had a load event on the same node, it could modify the name and id parameters and we only start
+            // the autosense loop (which is based on name and id) after the load event's action executed. 
+            // (Note, oper.node.id may lie in the log then and show the original, unchanged id but we ignore this for the time.)
+            var g = function() {
+                var f = function() {
+                    var func_to_bind = iloadoper.makeExecuteActionsHook();
+                    kukit.engine.bindScheduler.addPost(func_to_bind, 'Execute iload event for iframe ' + iloadoper.node.name);
+                };
+                new kukit.dom.EmbeddedContentLoadedScheduler(iloadoper.node.id, f, iloadoper.parms.autodetect);
+            }
+            kukit.engine.bindScheduler.addPost(g, 'Schedule iload event for iframe ' + iloadoper.node.name);
+        } else {
+            kukit.logDebug('EventRule #' + iloadoper.eventrule.getNr() + ' mergeid ' + iloadoper.eventrule.kss_selector.mergeid + ' event selected normal postponed execution.');
+            var func_to_bind = iloadoper.makeExecuteActionsHook();
+            kukit.engine.bindScheduler.addPost(func_to_bind, 'Execute iload event for iframe ' + iloadoper.node.name);
+        }
     }
 };
 
-kukit.eventsGlobalRegistry.register(null, 'load', kukit.pl.LoadEventBinder, '__bind__', null);
+// Use the "node" iterator to provide expected invocation and call signature of the bind method.
+kukit.eventsGlobalRegistry.registerForAllEvents(null, ['load', 'iload'], kukit.pl.LoadEventBinder, '__bind__', null, 'node');
+
 
 /*
 * class SpinnerEventBinder
@@ -499,7 +576,7 @@
 kukit.commandsGlobalRegistry.registerFromAction('addClassName', kukit.cr.makeSelectorCommand);
 
 kukit.actionsGlobalRegistry.register('removeClassName', function(oper) {
-    oper.completeParms(['name'], {}, 'addClassName action');
+    oper.completeParms(['name'], {}, 'removeClassName action');
     removeClassName(oper.node, oper.parms.name);
 });
 kukit.commandsGlobalRegistry.registerFromAction('removeClassName', kukit.cr.makeSelectorCommand);
@@ -635,6 +712,16 @@
 });
 kukit.commandsGlobalRegistry.registerFromAction('moveNodeAfter', kukit.cr.makeSelectorCommand);
 
+kukit.actionsGlobalRegistry.register('moveNodeBefore', function(oper) {
+    oper.completeParms(['html_id'], {}, 'moveNodeBefore action');
+    var node = oper.node;
+    var parentNode = node.parentNode;
+    parentNode.removeChild(node);
+    var toNode = document.getElementById(oper.parms.html_id);
+    parentNode.insertBefore(node, toNode);
+});
+kukit.commandsGlobalRegistry.registerFromAction('moveNodeBefore', kukit.cr.makeSelectorCommand);
+
 kukit.actionsGlobalRegistry.register('copyChildNodesFrom', function(oper) {
     oper.completeParms(['html_id'], {}, 'copyChildNodesFrom action');
     var fromNode = document.getElementById(oper.parms.html_id);
@@ -740,4 +827,4 @@
 kukit.commandsGlobalRegistry.registerFromAction('copyChildNodesFrom', kukit.cr.makeSelectorCommand, 'copyChildrenFrom');
 kukit.commandsGlobalRegistry.registerFromAction('copyChildNodesTo', kukit.cr.makeSelectorCommand, 'copyChildrenTo');
 kukit.commandsGlobalRegistry.registerFromAction('setStateVar', kukit.cr.makeGlobalCommand, 'setStatevar');
-   
+

Modified: kukit/kukit.js/trunk/kukit/utils.js
==============================================================================
--- kukit/kukit.js/trunk/kukit/utils.js	(original)
+++ kukit/kukit.js/trunk/kukit/utils.js	Fri May 11 13:01:14 2007
@@ -282,6 +282,7 @@
 kukit.ut.TimerCounter.prototype.clear = function() {
     if (this.timer) {
         window.clearTimeout(this.timer);
+        this.timer = null;
     }
     this.restart = false;
 };


More information about the Kukit-checkins mailing list