/* * Copyright (C) 2007 Diego Perini * All rights reserved. * * nwevents.js - Javascript Event Manager * * Author: Diego Perini * Version: 1.05 * Release: 20071218 * * License: * http://javascript.nwbox.com/NWEvents/MIT-LICENSE * Download: * http://javascript.nwbox.com/NWEvents/nwevents.js */ window.NW=window.NW||{}; NW.Event=function(){ var version='1.05', // phases constants, CAPTURING_PHASE not usable in IE CAPTURING_PHASE=1,AT_TARGET=2,BUBBLING_PHASE=3, // handlers collection Handlers={}, // listeners collection Listeners={}, // delegates collection Delegates={}, // get element context window getContext= function(o){ return (o.ownerDocument||o.document||o).parentWindow||window; }, // fix IE event properties to // best fit with w3c standards fixEvent= function(o,e){ e=e||getContext(o).event; // IE: emulate focus/blur bubbling if(/^focus(in|out)/i.test(e.type)){ e=getContext(o).document.createEventObject(e); // replace focus/blur with focusin/focusout e.type=(e.type=='focusin')?'focus':'blur'; } if(!e.target){ e.target=e.srcElement; } if(!e.currentTarget){ e.currentTarget=o; } if(!e.preventDefault){ e.preventDefault=preventDefault; } if(!e.stopPropagation){ e.stopPropagation=stopPropagation; } if(e.target&&(/3|4/).test(e.target.nodeType)){ e.target=e.target.parentNode; } if(!e.eventPhase){ e.eventPhase=e.target==o?AT_TARGET:BUBBLING_PHASE; } if(!e.relatedTarget&&e.toElement&&e.fromElement){ if(e.type=='mouseout'){e.relatedTarget=e.toElement;} if(e.type=='mouseover'){e.relatedTarget=e.fromElement;} } if(!e.which&&(e.charCode||e.keyCode)){ e.which=e.charCode||e.keyCode; } return e; }, // prevent default action preventDefault= function(){ this.returnValue=false; }, // stop event propagation stopPropagation= function(){ this.cancelBubble=true; }, // search handlers collection for // (o)bject, (t)ype, (h)andler, // return index or false isHandler= function(o,t,h){ var i,found=false; if(Handlers[t]&&Handlers[t].el){ for(i=0;Handlers[t].el.length>i;i++){ if(Handlers[t].el[i]===o&&Handlers[t].fn[i]===h){ found=i; break; } } } return found; }, // search listeners collection for // (o)bject, (t)ype, (h)andler, // return index or false isListener= function(o,t,h){ var i,found=false; if(Listeners[t]&&Listeners[t].el){ for(i=0;Listeners[t].el.length>i;i++){ if(Listeners[t].el[i]===o&&Listeners[t].fn[i]===h){ found=i; break; } } } return found; }, // search delegates collection for // (o)bject, (h)andler matching // delegates for (t)ype events isDelegate= function(o,t,h,d){ var i,n,found=false; for(i=0;Delegates[t].fn.length>i;i++){ n=0; if(Delegates[t].fn[i]===h){ if(typeof o=='string'){ // a selector string if(Delegates[t].el[i]===o&&Delegates[t].de[i]===d){ found=i;break; } }else{ // an object reference if(Delegates[t].el[i].id) {n++;if(o.id.toLowerCase()== Delegates[t].el[i].id. toLowerCase()){n--;}} else if(Delegates[t].el[i].nodeName) {n++;if(o.nodeName.toLowerCase()== Delegates[t].el[i].nodeName. toLowerCase()){n--;}} else if(Delegates[t].el[i].className) {n++;if((' '+o.className+' '). replace(/\s+/g,' ').indexOf(' '+ Delegates[t].el[i].className+' ')){n--;}} if(n===0){found=i;break;} } } } return found; }, // handle listeners chain for (e)vent type handleListeners= function(e){ var i,f,t=e.type; if(Listeners[t]){ // make a copy of the Listeners[type] array // since it can be modified run time by the // events deleting themselves or adding new f=Listeners[t].fn.slice(); for(i=0;f.length>i;i++){ // element match current target ? if(Listeners[t].el[i]===this){ f[i].call(this,e); } } } }, // handle delegates chain for (e)vent type handleDelegates= function(e){ var i,j,f,o,m,t=e.type; if(Delegates[t]){ d=Delegates[t].de.slice(); o=Delegates[t].el.slice(); f=Delegates[t].fn.slice(); // process chain in fifo order for(i=0;f.length>i;i++){ if(typeof o[i]=='string'){ // fix nasty load order bug // test nwdomlib has loaded if(window.NW&&window.NW.Dom){ // selector string "matcher" if(window.NW.Dom.match(e.target,o[i])){ f[i].call(e.target,e); } } }else{ // real DOM element reference if(o[i]===e.target){ f[i].call(e.target,e); }else{ // selector object prop/values m=true; for(j in o[i]){ if(o[i][j]!==e.target[j]){ m=false; break; } } if(m){ f[i].call(e.target,e); } } } } } }, // search listeners collection for // (o)bject, (t)ype, (h)andler, // return array getListeners= function(o,t,h){ var i,r=[]; if(Listeners[t]&&Listeners[t].el){ for(i=0;Listeners[t].el.length>i;i++){ if(Listeners[t].el[i]===o&&Listeners[t].fn[i]===h){ r.push(Listeners[t].fn[i]); } } } return r; }, // return delegate useCount to // ensure that only one handler // is set for each type delegate getDelegates= function(t,d){ for(var n=0,i=0;Delegates[t].de.length>i;i++){ if(Delegates[t].de[i]===d){ n++; } } return n; }; // ********** begin public methods ********** return { // use event registration (DOM2) or inline events (DOM0) EVENTS_W3C:true, // block any further event processing stop: function(e){ if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue=false; } if(e.stopPropagation){ e.stopPropagation(); }else{ e.cancelBubble=true; } }, // append event handler // (o)bject, (t)ype, (h)andler, (c)apture appendHandler: function(o,t,h,c){ var key; // passed handler not in the chain if((key=isHandler(o,t,h))===false){ // create handlers storage for (t)ype Handlers[t]=Handlers[t]||{el:[],fn:[],wr:[]}; if(o.addEventListener&&NW.Event.EVENTS_W3C){ o.addEventListener(t,h,c||false); }else if(o.attachEvent&&NW.Event.EVENTS_W3C){ // only for IE add to wrappers array key=Handlers[t].wr.push( function(e){ return h.call(o,fixEvent(o,e)); } ); o.attachEvent('on'+t,Handlers[t].wr[key-1]); }else{ // first handler for (t)ype if(Handlers[t].el.length===0){ // save old handler if any if(typeof o['on'+t]=='function'){ // add old object to the elements array Handlers[t].el.push(o); // add old handler to the function array Handlers[t].fn.push(o['on'+t]); } o['on'+t]= function(e){ return h.call(this,fixEvent(this,e)); }; } } // add new object to the elements array Handlers[t].el.push(o); // add new handler to the function array Handlers[t].fn.push(h); return true; } return false; }, // remove event handler // (o)bject, (t)ype, (h)andler, (c)apture removeHandler: function(o,t,h,c){ var key; if((key=isHandler(o,t,h))!==false){ // remove event handler/listener from chain if(o.removeEventListener&&NW.Event.EVENTS_W3C){ o.removeEventListener(t,h,c||false); }else if(o.detachEvent&&NW.Event.EVENTS_W3C){ o.detachEvent('on'+t,Handlers[t].wr[key]); // remove wrapper from chain Handlers[t].wr.splice(key,1); }else{ if(Handlers[t].el.length==1){ o['on'+t]=Handlers[t].el[0]; } } // remove element from chain Handlers[t].el.splice(key,1); // remove function from chain Handlers[t].fn.splice(key,1); // no more handlers for this type if(Handlers[t].el.length==1){ // remove element from chain Handlers[t].el.splice(0,1); // remove function from chain Handlers[t].fn.splice(0,1); // delete handlers chain type delete Handlers[t]; } return true; } return false; }, // append listener to chain // (o)bject, (t)ype, (h)andler, (c)apture appendListener: function(o,t,h,c){ var key; if((key=isListener(o,t,h))===false){ // create listeners storage for event type Listeners[t]=Listeners[t]||{el:[],fn:[]}; if(getListeners(o,t,h).length===0){ // first listener, append the handler NW.Event.appendHandler(o,t,handleListeners,c); } // add listener to the chain Listeners[t].el.push(o); Listeners[t].fn.push(h); return true; } return false; }, // remove listener from chain // (o)bject, (t)ype, (h)andler, (c)apture removeListener: function(o,t,h,c){ var key; if((key=isListener(o,t,h))!==false){ // remove listener from the chain Listeners[t].fn.splice(key,1); Listeners[t].el.splice(key,1); // no more listeners for this type if(Listeners[t].el.length===0){ // remove the real handler for this chain NW.Event.removeHandler(o,t,handleListeners,c); // delete listeners chain type delete Listeners[t]; } return true; } return false; }, // append delegate to chain // (o)bject, (t)ype, (h)andler, (d)elegate appendDelegate: function(o,t,h,d){ d=d||document.documentElement; Delegates[t]=Delegates[t]||{el:[],fn:[],de:[]}; if(isDelegate(o,t,h,d)===false){ // add delegate to the chain Delegates[t].el.push(o); Delegates[t].fn.push(h); Delegates[t].de.push(d); // first delegate for this event type if(getDelegates(t,d)==1){ // IE: use bubbling clones of UI activation events (focus/blur) if(typeof document.fileSize!='undefined'&&/focus|blur/.test(t)){ t='focus'+((t=='focus')?'in':'out'); } NW.Event.appendHandler(d,t,handleDelegates, /focus|blur/i.test(t)?true:false); } return true; } return false; }, // remove delegate from chain // (o)bject, (t)ype, (h)andler, (d)elegate removeDelegate: function(o,t,h,d){ var i,key; if(Delegates[t]){ d=d||document.documentElement; key=isDelegate(o,t,h,d); if(key!==false){ // remove delegate listener from the chain Delegates[t].el.splice(key,1); Delegates[t].fn.splice(key,1); Delegates[t].de.splice(key,1); if(Delegates[t].fn.length===0){ // IE: use bubbling clones of UI activation events (focus/blur) if(typeof document.fileSize!='undefined'&&/focus|blur/.test(t)){ t='focus'+((t=='focus')?'in':'out'); } // remove the real event handler for this chain NW.Event.removeHandler(d,t,handleDelegates, /focus|blur/i.test(t)?true:false); // remove chain for this type Delegates[t]=null; // check if some chain is still active for(i in Delegates){return true;} // if not cleanup delegate list Delegates=null; } return true; } } return false; } } }();