1/** 2 * Copyright 2009 Tim Down. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18if(!Array.prototype.push){Array.prototype.push=function(){for(var i=0,len=arguments.length;i<len;i++){this[this.length]=arguments[i];} 19return this.length;};} 20if(!Array.prototype.shift){Array.prototype.shift=function(){if(this.length>0){var firstItem=this[0];for(var i=0,len=this.length-1;i<len;i++){this[i]=this[i+1];} 21this.length=this.length-1;return firstItem;}};} 22if(!Array.prototype.splice){Array.prototype.splice=function(startIndex,deleteCount){var itemsAfterDeleted=this.slice(startIndex+deleteCount);var itemsDeleted=this.slice(startIndex,startIndex+deleteCount);this.length=startIndex;var argumentsArray=[];for(var i=0,len=arguments.length;i<len;i++){argumentsArray[i]=arguments[i];} 23var itemsToAppend=(argumentsArray.length>2)?itemsAfterDeleted=argumentsArray.slice(2).concat(itemsAfterDeleted):itemsAfterDeleted;for(i=0,len=itemsToAppend.length;i<len;i++){this.push(itemsToAppend[i]);} 24return itemsDeleted;};} 25var log4javascript;(function(){function isUndefined(obj){return typeof obj=="undefined";} 26function EventSupport(){} 27EventSupport.prototype={eventTypes:[],eventListeners:{},setEventTypes:function(eventTypesParam){if(eventTypesParam instanceof Array){this.eventTypes=eventTypesParam;this.eventListeners={};for(var i=0,len=this.eventTypes.length;i<len;i++){this.eventListeners[this.eventTypes[i]]=[];}}else{handleError("log4javascript.EventSupport ["+this+"]: setEventTypes: eventTypes parameter must be an Array");}},addEventListener:function(eventType,listener){if(typeof listener=="function"){if(!array_contains(this.eventTypes,eventType)){handleError("log4javascript.EventSupport ["+this+"]: addEventListener: no event called '"+eventType+"'");} 28this.eventListeners[eventType].push(listener);}else{handleError("log4javascript.EventSupport ["+this+"]: addEventListener: listener must be a function");}},removeEventListener:function(eventType,listener){if(typeof listener=="function"){if(!array_contains(this.eventTypes,eventType)){handleError("log4javascript.EventSupport ["+this+"]: removeEventListener: no event called '"+eventType+"'");} 29array_remove(this.eventListeners[eventType],listener);}else{handleError("log4javascript.EventSupport ["+this+"]: removeEventListener: listener must be a function");}},dispatchEvent:function(eventType,eventArgs){if(array_contains(this.eventTypes,eventType)){var listeners=this.eventListeners[eventType];for(var i=0,len=listeners.length;i<len;i++){listeners[i](this,eventType,eventArgs);}}else{handleError("log4javascript.EventSupport ["+this+"]: dispatchEvent: no event called '"+eventType+"'");}}};var applicationStartDate=new Date();var uniqueId="log4javascript_"+applicationStartDate.getTime()+"_"+ 30Math.floor(Math.random()*100000000);var emptyFunction=function(){};var newLine="\r\n";var pageLoaded=false;function Log4JavaScript(){} 31Log4JavaScript.prototype=new EventSupport();log4javascript=new Log4JavaScript();log4javascript.version="1.4.1";log4javascript.edition="log4javascript";function toStr(obj){if(obj&&obj.toString){return obj.toString();}else{return String(obj);}} 32function getExceptionMessage(ex){if(ex.message){return ex.message;}else if(ex.description){return ex.description;}else{return toStr(ex);}} 33function getUrlFileName(url){var lastSlashIndex=Math.max(url.lastIndexOf("/"),url.lastIndexOf("\\"));return url.substr(lastSlashIndex+1);} 34function getExceptionStringRep(ex){if(ex){var exStr="Exception: "+getExceptionMessage(ex);try{if(ex.lineNumber){exStr+=" on line number "+ex.lineNumber;} 35if(ex.fileName){exStr+=" in file "+getUrlFileName(ex.fileName);}}catch(localEx){logLog.warn("Unable to obtain file and line information for error");} 36if(showStackTraces&&ex.stack){exStr+=newLine+"Stack trace:"+newLine+ex.stack;} 37return exStr;} 38return null;} 39function bool(obj){return Boolean(obj);} 40function trim(str){return str.replace(/^\s+/,"").replace(/\s+$/,"");} 41function splitIntoLines(text){var text2=text.replace(/\r\n/g,"\n").replace(/\r/g,"\n");return text2.split("\n");} 42function urlEncode(str){return escape(str).replace(/\+/g,"%2B").replace(/"/g,"%22").replace(/'/g,"%27").replace(/\//g,"%2F").replace(/=/g,"%3D");} 43function urlDecode(str){return unescape(str).replace(/%2B/g,"+").replace(/%22/g,"\"").replace(/%27/g,"'").replace(/%2F/g,"/").replace(/%3D/g,"=");} 44function array_remove(arr,val){var index=-1;for(var i=0,len=arr.length;i<len;i++){if(arr[i]===val){index=i;break;}} 45if(index>=0){arr.splice(index,1);return true;}else{return false;}} 46function array_contains(arr,val){for(var i=0,len=arr.length;i<len;i++){if(arr[i]==val){return true;}} 47return false;} 48function extractBooleanFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue;}else{return bool(param);}} 49function extractStringFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue;}else{return String(param);}} 50function extractIntFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue;}else{try{var value=parseInt(param,10);return isNaN(value)?defaultValue:value;}catch(ex){logLog.warn("Invalid int param "+param,ex);return defaultValue;}}} 51function extractFunctionFromParam(param,defaultValue){if(typeof param=="function"){return param;}else{return defaultValue;}} 52function isError(err){return(err instanceof Error);} 53if(!Function.prototype.apply){Function.prototype.apply=function(obj,args){var methodName="__apply__";if(typeof obj[methodName]!="undefined"){methodName+=String(Math.random()).substr(2);} 54obj[methodName]=this;var argsStrings=[];for(var i=0,len=args.length;i<len;i++){argsStrings[i]="args["+i+"]";} 55var script="obj."+methodName+"("+argsStrings.join(",")+")";var returnValue=eval(script);delete obj[methodName];return returnValue;};} 56if(!Function.prototype.call){Function.prototype.call=function(obj){var args=[];for(var i=1,len=arguments.length;i<len;i++){args[i-1]=arguments[i];} 57return this.apply(obj,args);};} 58function getListenersPropertyName(eventName){return"__log4javascript_listeners__"+eventName;} 59function addEvent(node,eventName,listener,useCapture,win){win=win?win:window;if(node.addEventListener){node.addEventListener(eventName,listener,useCapture);}else if(node.attachEvent){node.attachEvent("on"+eventName,listener);}else{var propertyName=getListenersPropertyName(eventName);if(!node[propertyName]){node[propertyName]=[];node["on"+eventName]=function(evt){evt=getEvent(evt,win);var listenersPropertyName=getListenersPropertyName(eventName);var listeners=this[listenersPropertyName].concat([]);var currentListener;while((currentListener=listeners.shift())){currentListener.call(this,evt);}};} 60node[propertyName].push(listener);}} 61function removeEvent(node,eventName,listener,useCapture){if(node.removeEventListener){node.removeEventListener(eventName,listener,useCapture);}else if(node.detachEvent){node.detachEvent("on"+eventName,listener);}else{var propertyName=getListenersPropertyName(eventName);if(node[propertyName]){array_remove(node[propertyName],listener);}}} 62function getEvent(evt,win){win=win?win:window;return evt?evt:win.event;} 63function stopEventPropagation(evt){if(evt.stopPropagation){evt.stopPropagation();}else if(typeof evt.cancelBubble!="undefined"){evt.cancelBubble=true;} 64evt.returnValue=false;} 65var logLog={quietMode:false,debugMessages:[],setQuietMode:function(quietMode){this.quietMode=bool(quietMode);},numberOfErrors:0,alertAllErrors:false,setAlertAllErrors:function(alertAllErrors){this.alertAllErrors=alertAllErrors;},debug:function(message){this.debugMessages.push(message);},displayDebug:function(){alert(this.debugMessages.join(newLine));},warn:function(message,exception){},error:function(message,exception){if(++this.numberOfErrors==1||this.alertAllErrors){if(!this.quietMode){var alertMessage="log4javascript error: "+message;if(exception){alertMessage+=newLine+newLine+"Original error: "+getExceptionStringRep(exception);} 66alert(alertMessage);}}}};log4javascript.logLog=logLog;log4javascript.setEventTypes(["load","error"]);function handleError(message,exception){logLog.error(message,exception);log4javascript.dispatchEvent("error",{"message":message,"exception":exception});} 67log4javascript.handleError=handleError;var enabled=!((typeof log4javascript_disabled!="undefined")&&log4javascript_disabled);log4javascript.setEnabled=function(enable){enabled=bool(enable);};log4javascript.isEnabled=function(){return enabled;};var useTimeStampsInMilliseconds=true;log4javascript.setTimeStampsInMilliseconds=function(timeStampsInMilliseconds){useTimeStampsInMilliseconds=bool(timeStampsInMilliseconds);};log4javascript.isTimeStampsInMilliseconds=function(){return useTimeStampsInMilliseconds;};log4javascript.evalInScope=function(expr){return eval(expr);};var showStackTraces=false;log4javascript.setShowStackTraces=function(show){showStackTraces=bool(show);};var Level=function(level,name){this.level=level;this.name=name;};Level.prototype={toString:function(){return this.name;},equals:function(level){return this.level==level.level;},isGreaterOrEqual:function(level){return this.level>=level.level;}};Level.ALL=new Level(Number.MIN_VALUE,"ALL");Level.TRACE=new Level(10000,"TRACE");Level.DEBUG=new Level(20000,"DEBUG");Level.INFO=new Level(30000,"INFO");Level.WARN=new Level(40000,"WARN");Level.ERROR=new Level(50000,"ERROR");Level.FATAL=new Level(60000,"FATAL");Level.OFF=new Level(Number.MAX_VALUE,"OFF");log4javascript.Level=Level;function Timer(name,level){this.name=name;this.level=isUndefined(level)?Level.INFO:level;this.start=new Date();} 68Timer.prototype.getElapsedTime=function(){return new Date().getTime()-this.start.getTime();};var anonymousLoggerName="[anonymous]";var defaultLoggerName="[default]";var nullLoggerName="[null]";var rootLoggerName="root";function Logger(name){this.name=name;this.parent=null;this.children=[];var appenders=[];var loggerLevel=null;
68var isRoot=(this.name===rootLoggerName);var isNull=(this.name===nullLoggerName);var appenderCache=null;var appenderCacheInvalidated=false;this.addChild=function(childLogger){this.children.push(childLogger);childLogger.parent=this;childLogger.invalidateAppenderCache();};var additive=true;this.getAdditivity=function(){return additive;};this.setAdditivity=function(additivity){var valueChanged=(additive!=additivity);additive=additivity;if(valueChanged){this.invalidateAppenderCache();}};this.addAppender=function(appender){if(isNull){handleError("Logger.addAppender: you may not add an appender to the null logger");}else{if(appender instanceof log4javascript.Appender){if(!array_contains(appenders,appender)){appenders.push(appender);appender.setAddedToLogger(this);this.invalidateAppenderCache();}}else{handleError("Logger.addAppender: appender supplied ('"+ 69toStr(appender)+"') is not a subclass of Appender");}}};this.removeAppender=function(appender){array_remove(appenders,appender);appender.setRemovedFromLogger(this);this.invalidateAppenderCache();};this.removeAllAppenders=function(){var appenderCount=appenders.length;if(appenderCount>0){for(var i=0;i<appenderCount;i++){appenders[i].setRemovedFromLogger(this);} 70appenders.length=0;this.invalidateAppenderCache();}};this.getEffectiveAppenders=function(){if(appenderCache===null||appenderCacheInvalidated){var parentEffectiveAppenders=(isRoot||!this.getAdditivity())?[]:this.parent.getEffectiveAppenders();appenderCache=parentEffectiveAppenders.concat(appenders);appenderCacheInvalidated=false;} 71return appenderCache;};this.invalidateAppenderCache=function(){appenderCacheInvalidated=true;for(var i=0,len=this.children.length;i<len;i++){this.children[i].invalidateAppenderCache();}};this.log=function(level,params){if(level.isGreaterOrEqual(this.getEffectiveLevel())){var exception;var finalParamIndex=params.length-1;var lastParam=params[params.length-1];if(params.length>1&&isError(lastParam)){exception=lastParam;finalParamIndex--;} 72var messages=[];for(var i=0;i<=finalParamIndex;i++){messages[i]=params[i];} 73var loggingEvent=new LoggingEvent(this,new Date(),level,messages,exception);this.callAppenders(loggingEvent);}};this.callAppenders=function(loggingEvent){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].doAppend(loggingEvent);}};this.setLevel=function(level){if(isRoot&&level===null){handleError("Logger.setLevel: you cannot set the level of the root logger to null");}else if(level instanceof Level){loggerLevel=level;}else{handleError("Logger.setLevel: level supplied to logger "+ 74this.name+" is not an instance of log4javascript.Level");}};this.getLevel=function(){return loggerLevel;};this.getEffectiveLevel=function(){for(var logger=this;logger!==null;logger=logger.parent){var level=logger.getLevel();if(level!==null){return level;}}};this.group=function(name,initiallyExpanded){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].group(name,initiallyExpanded);}};this.groupEnd=function(name){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].groupEnd();}};var timers={};this.time=function(name,level){if(isUndefined(name)){handleError("Logger.time: a name for the timer must be supplied");}else if(level&&!(level instanceof Level)){handleError("Logger.time: level supplied to timer "+ 75name+" is not an instance of log4javascript.Level");}else{timers[name]=new Timer(name,level);}};this.timeEnd=function(name){if(isUndefined(name)){handleError("Logger.timeEnd: a name for the timer must be supplied");}else if(timers[name]){var timer=timers[name];var milliseconds=timer.getElapsedTime();this.log(timer.level,["Timer "+toStr(name)+" completed in "+milliseconds+"ms"]);delete timers[name];}else{logLog.warn("Logger.timeEnd: no timer found with name "+name);}};this.assert=function(expr){if(!expr){var args=[];for(var i=1,len=arguments.length;i<len;i++){args.push(arguments[i]);} 76args=(args.length>0)?args:["Assertion Failure"];args.push(newLine);args.push(expr);this.log(Level.ERROR,args);}};this.toString=function(){return"Logger["+this.name+"]";};} 77Logger.prototype={trace:function(){this.log(Level.TRACE,arguments);},debug:function(){this.log(Level.DEBUG,arguments);},info:function(){this.log(Level.INFO,arguments);},warn:function(){this.log(Level.WARN,arguments);},error:function(){this.log(Level.ERROR,arguments);},fatal:function(){this.log(Level.FATAL,arguments);},isEnabledFor:function(level){return level.isGreaterOrEqual(this.getEffectiveLevel());},isTraceEnabled:function(){return this.isEnabledFor(Level.TRACE);},isDebugEnabled:function(){return this.isEnabledFor(Level.DEBUG);},isInfoEnabled:function(){return this.isEnabledFor(Level.INFO);},isWarnEnabled:function(){return this.isEnabledFor(Level.WARN);},isErrorEnabled:function(){return this.isEnabledFor(Level.ERROR);},isFatalEnabled:function(){return this.isEnabledFor(Level.FATAL);}};Logger.prototype.trace.isEntryPoint=true;Logger.prototype.debug.isEntryPoint=true;Logger.prototype.info.isEntryPoint=true;Logger.prototype.warn.isEntryPoint=true;Logger.prototype.error.isEntryPoint=true;Logger.prototype.fatal.isEntryPoint=true;var loggers={};var loggerNames=[];var ROOT_LOGGER_DEFAULT_LEVEL=Level.DEBUG;var rootLogger=new Logger(rootLoggerName);rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);log4javascript.getRootLogger=function(){return rootLogger;};log4javascript.getLogger=function(loggerName){if(!(typeof loggerName=="string")){loggerName=anonymousLoggerName;logLog.warn("log4javascript.getLogger: non-string logger name "+
78toStr(loggerName)+" supplied, returning anonymous logger");} 79if(loggerName==rootLoggerName){handleError("log4javascript.getLogger: root logger may not be obtained by name");} 80if(!loggers[loggerName]){var logger=new Logger(loggerName);loggers[loggerName]=logger;loggerNames.push(loggerName);var lastDotIndex=loggerName.lastIndexOf(".");var parentLogger;if(lastDotIndex>-1){var parentLoggerName=loggerName.substring(0,lastDotIndex);parentLogger=log4javascript.getLogger(parentLoggerName);}else{parentLogger=rootLogger;} 81parentLogger.addChild(logger);} 82return loggers[loggerName];};var defaultLogger=null;log4javascript.getDefaultLogger=function(){if(!defaultLogger){defaultLogger=log4javascript.getLogger(defaultLoggerName);var a=new log4javascript.PopUpAppender();defaultLogger.addAppender(a);} 83return defaultLogger;};var nullLogger=null;log4javascript.getNullLogger=function(){if(!nullLogger){nullLogger=new Logger(nullLoggerName);nullLogger.setLevel(Level.OFF);} 84return nullLogger;};log4javascript.resetConfiguration=function(){rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);loggers={};};var LoggingEvent=function(logger,timeStamp,level,messages,exception){this.logger=logger;this.timeStamp=timeStamp;this.timeStampInMilliseconds=timeStamp.getTime();this.timeStampInSeconds=Math.floor(this.timeStampInMilliseconds/1000);this.milliseconds=this.timeStamp.getMilliseconds();this.level=level;this.messages=messages;this.exception=exception;};LoggingEvent.prototype={getThrowableStrRep:function(){return this.exception?getExceptionStringRep(this.exception):"";},getCombinedMessages:function(){return(this.messages.length==1)?this.messages[0]:this.messages.join(newLine);},toString:function(){return"LoggingEvent["+this.level+"]";}};log4javascript.LoggingEvent=LoggingEvent;var Layout=function(){};Layout.prototype={defaults:{loggerKey:"logger",timeStampKey:"timestamp",millisecondsKey:"milliseconds",levelKey:"level",messageKey:"message",exceptionKey:"exception",urlKey:"url"},loggerKey:"logger",timeStampKey:"timestamp",millisecondsKey:"milliseconds",levelKey:"level",messageKey:"message",exceptionKey:"exception",urlKey:"url",batchHeader:"",batchFooter:"",batchSeparator:"",returnsPostData:false,overrideTimeStampsSetting:false,useTimeStampsInMilliseconds:null,format:function(loggingEvent){handleError("Layout.format: layout supplied has no format() method");},ignoresThrowable:function(){handleError("Layout.ignoresThrowable: layout supplied has no ignoresThrowable() method");},getContentType:function(){return"text/plain";},allowBatching:function(){return true;},setTimeStampsInMilliseconds:function(timeStampsInMilliseconds){this.overrideTimeStampsSetting=true;this.useTimeStampsInMilliseconds=bool(timeStampsInMilliseconds);},isTimeStampsInMilliseconds:function(){return this.overrideTimeStampsSetting?this.useTimeStampsInMilliseconds:useTimeStampsInMilliseconds;},getTimeStampValue:function(loggingEvent){return this.isTimeStampsInMilliseconds()?loggingEvent.timeStampInMilliseconds:loggingEvent.timeStampInSeconds;},getDataValues:function(loggingEvent,combineMessages){var dataValues=[[this.loggerKey,loggingEvent.logger.name],[this.timeStampKey,this.getTimeStampValue(loggingEvent)],[this.levelKey,loggingEvent.level.name],[this.urlKey,window.location.href],[this.messageKey,combineMessages?loggingEvent.getCombinedMessages():loggingEvent.messages]];if(!this.isTimeStampsInMilliseconds()){dataValues.push([this.millisecondsKey,loggingEvent.milliseconds]);} 85if(loggingEvent.exception){dataValues.push([this.exceptionKey,getExceptionStringRep(loggingEvent.exception)]);} 86if(this.hasCustomFields()){for(var i=0,len=this.customFields.length;i<len;i++){var val=this.customFields[i].value;if(typeof val==="function"){val=val(this,loggingEvent);} 87dataValues.push([this.customFields[i].name,val]);}} 88return dataValues;},setKeys:function(loggerKey,timeStampKey,levelKey,messageKey,exceptionKey,urlKey,millisecondsKey){this.loggerKey=extractStringFromParam(loggerKey,this.defaults.loggerKey);this.timeStampKey=extractStringFromParam(timeStampKey,this.defaults.timeStampKey);this.levelKey=extractStringFromParam(levelKey,this.defaults.levelKey);this.messageKey=extractStringFromParam(messageKey,this.defaults.messageKey);this.exceptionKey=extractStringFromParam(exceptionKey,this.defaults.exceptionKey);this.urlKey=extractStringFromParam(urlKey,this.defaults.urlKey);this.millisecondsKey=extractStringFromParam(millisecondsKey,this.defaults.millisecondsKey);},setCustomField:function(name,value){var fieldUpdated=false;for(var i=0,len=this.customFields.length;i<len;i++){if(this.customFields[i].name===name){this.customFields[i].value=value;fieldUpdated=true;}} 89if(!fieldUpdated){this.customFields.push({"name":name,"value":value});}},hasCustomFields:function(){return(this.customFields.length>0);},toString:function(){handleError("Layout.toString: all layouts must override this method");}};log4javascript.Layout=Layout;var Appender=function(){};Appender.prototype=new EventSupport();Appender.prototype.layout=new PatternLayout();Appender.prototype.threshold=Level.ALL;Appender.prototype.loggers=[];Appender.prototype.doAppend=function(loggingEvent){if(enabled&&loggingEvent.level.level>=this.threshold.level){this.append(loggingEvent);}};Appender.prototype.append=function(loggingEvent){};Appender.prototype.setLayout=function(layout){if(layout instanceof Layout){this.layout=layout;}else{handleError("Appender.setLayout: layout supplied to "+ 90this.toString()+" is not a subclass of Layout");}};Appender.prototype.getLayout=function(){return this.layout;};Appender.prototype.setThreshold=function(threshold){if(threshold instanceof Level){this.threshold=threshold;}else{handleError("Appender.setThreshold: threshold supplied to "+ 91this.toString()+" is not a subclass of Level");}};Appender.prototype.getThreshold=function(){return this.threshold;};Appender.prototype.setAddedToLogger=function(logger){this.loggers.push(logger);};Appender.prototype.setRemovedFromLogger=function(logger){array_remove(this.loggers,logger);};Appender.prototype.group=emptyFunction;Appender.prototype.groupEnd=emptyFunction;Appender.prototype.toString=function(){handleError("Appender.toString: all appenders must override this method");};log4javascript.Appender=Appender;function SimpleLayout(){this.customFields=[];} 92SimpleLayout.prototype=new Layout();SimpleLayout.prototype.format=function(loggingEvent){return loggingEvent.level.name+" - "+loggingEvent.getCombinedMessages();};SimpleLayout.prototype.ignoresThrowable=function(){return true;};SimpleLayout.prototype.toString=function(){return"SimpleLayout";};log4javascript.SimpleLayout=SimpleLayout;function NullLayout(){this.customFields=[];} 93NullLayout.prototype=new Layout();NullLayout.prototype.format=function(loggingEvent){return loggingEvent.messages;};NullLayout.prototype.ignoresThrowable=function(){return true;};NullLayout.prototype.toString=function(){return"NullLayout";};log4javascript.NullLayout=NullLayout;function XmlLayout(combineMessages){this.combineMessages=extractBooleanFromParam(combineMessages,true);this.customFields=[];} 94XmlLayout.prototype=new Layout();XmlLayout.prototype.isCombinedMessages=function(){return this.combineMessages;};XmlLayout.prototype.getContentType=function(){return"text/xml";};XmlLayout.prototype.escapeCdata=function(str){return str.replace(/\]\]>/,"]]>]]><![CDATA[");};XmlLayout.prototype.format=function(loggingEvent){var layout=this;var i,len;function formatMessage(message){message=(typeof message==="string")?message:toStr(message);return"<log4javascript:message><![CDATA["+ 95layout.escapeCdata(message)+"]]></log4javascript:message>";} 96var str="<log4javascript:event logger=\""+loggingEvent.logger.name+"\" timestamp=\""+this.getTimeStampValue(loggingEvent)+"\"";if(!this.isTimeStampsInMilliseconds()){str+=" milliseconds=\""+loggingEvent.milliseconds+"\"";} 97str+=" level=\""+loggingEvent.level.name+"\">"+newLine;if(this.combineMessages){str+=formatMessage(loggingEvent.getCombinedMessages());}else{str+="<log4javascript:messages>"+newLine;for(i=0,len=loggingEvent.messages.length;i<len;i++){str+=formatMessage(loggingEvent.messages[i])+newLine;} 98str+="</log4javascript:messages>"+newLine;} 99if(this.hasCustomFields()){for(i=0,len=this.customFields.length;i<len;i++){str+="<log4javascript:customfield name=\""+ 100this.customFields[i].name+"\"><![CDATA["+ 101this.customFields[i].value.toString()+"]]></log4javascript:customfield>"+newLine;}} 102if(loggingEvent.exception){str+="<log4javascript:exception><![CDATA["+ 103getExceptionStringRep(loggingEvent.exception)+"]]></log4javascript:exception>"+newLine;} 104str+="</log4javascript:event>"+newLine+newLine;return str;};XmlLayout.prototype.ignoresThrowable=function(){return false;};XmlLayout.prototype.toString=function(){return"XmlLayout";};log4javascript.XmlLayout=XmlLayout;function escapeNewLines(str){return str.replace(/\r\n|\r|\n/g,"\\r\\n");} 105function JsonLayout(readable,combineMessages){this.readable=extractBooleanFromParam(readable,false);this.combineMessages=extractBooleanFromParam(combineMessages,true);this.batchHeader=this.readable?"["+newLine:"[";this.batchFooter=this.readable?"]"+newLine:"]";this.batchSeparator=this.readable?","+newLine:",";this.setKeys();this.colon=this.readable?": ":":";this.tab=this.readable?"\t":"";this.lineBreak=this.readable?newLine:"";this.customFields=[];} 106JsonLayout.prototype=new Layout();JsonLayout.prototype.isReadable=function(){return this.readable;};JsonLayout.prototype.isCombinedMessages=function(){return this.combineMessages;};JsonLayout.prototype.format=function(loggingEvent){var layout=this;var dataValues=this.getDataValues(loggingEvent,this.combineMessages);var str="{"+this.lineBreak;var i;function formatValue(val,prefix,expand){var formattedValue;var valType=typeof val;if(val instanceof Date){formattedValue=String(val.getTime());}else if(expand&&(val instanceof Array)){formattedValue="["+layout.lineBreak;for(i=0,len=val.length;i<len;i++){var childPrefix=prefix+layout.tab;formattedValue+=childPrefix+formatValue(val[i],childPrefix,false);
106if(i<val.length-1){formattedValue+=",";} 107formattedValue+=layout.lineBreak;} 108formattedValue+=prefix+"]";}else if(valType!=="number"&&valType!=="boolean"){formattedValue="\""+escapeNewLines(toStr(val).replace(/\"/g,"\\\""))+"\"";}else{formattedValue=val;} 109return formattedValue;} 110for(i=0,len=dataValues.length;i<len;i++){str+=this.tab+"\""+dataValues[i][0]+"\""+this.colon+formatValue(dataValues[i][1],this.tab,true);if(i<dataValues.length-1){str+=",";} 111str+=this.lineBreak;} 112str+="}"+this.lineBreak;return str;};JsonLayout.prototype.ignoresThrowable=function(){return false;};JsonLayout.prototype.toString=function(){return"JsonLayout";};JsonLayout.prototype.getContentType=function(){return"application/json";};log4javascript.JsonLayout=JsonLayout;function HttpPostDataLayout(){this.setKeys();this.customFields=[];this.returnsPostData=true;} 113HttpPostDataLayout.prototype=new Layout();HttpPostDataLayout.prototype.allowBatching=function(){return false;};HttpPostDataLayout.prototype.format=function(loggingEvent){var dataValues=this.getDataValues(loggingEvent);var queryBits=[];for(var i=0,len=dataValues.length;i<len;i++){var val=(dataValues[i][1]instanceof Date)?String(dataValues[i][1].getTime()):dataValues[i][1];queryBits.push(urlEncode(dataValues[i][0])+"="+urlEncode(val));} 114return queryBits.join("&");};HttpPostDataLayout.prototype.ignoresThrowable=function(loggingEvent){return false;};HttpPostDataLayout.prototype.toString=function(){return"HttpPostDataLayout";};log4javascript.HttpPostDataLayout=HttpPostDataLayout;function formatObjectExpansion(obj,depth,indentation){var objectsExpanded=[];function doFormat(obj,depth,indentation){var i,j,len,childDepth,childIndentation,childLines,expansion,childExpansion;if(!indentation){indentation="";} 115function formatString(text){var lines=splitIntoLines(text);for(var j=1,jLen=lines.length;j<jLen;j++){lines[j]=indentation+lines[j];} 116return lines.join(newLine);} 117if(obj===null){return"null";}else if(typeof obj=="undefined"){return"undefined";}else if(typeof obj=="string"){return formatString(obj);}else if(typeof obj=="object"&&array_contains(objectsExpanded,obj)){try{expansion=toStr(obj);}catch(ex){expansion="Error formatting property. Details: "+getExceptionStringRep(ex);} 118return expansion+" [already expanded]";}else if((obj instanceof Array)&&depth>0){objectsExpanded.push(obj);expansion="["+newLine;childDepth=depth-1;childIndentation=indentation+" ";childLines=[];for(i=0,len=obj.length;i<len;i++){try{childExpansion=doFormat(obj[i],childDepth,childIndentation);childLines.push(childIndentation+childExpansion);}catch(ex){childLines.push(childIndentation+"Error formatting array member. Details: "+ 119getExceptionStringRep(ex)+"");}} 120expansion+=childLines.join(","+newLine)+newLine+indentation+"]";return expansion;}else if(typeof obj=="object"&&depth>0){objectsExpanded.push(obj);expansion="{"+newLine;childDepth=depth-1;childIndentation=indentation+" ";childLines=[];for(i in obj){try{childExpansion=doFormat(obj[i],childDepth,childIndentation);childLines.push(childIndentation+i+": "+childExpansion);}catch(ex){childLines.push(childIndentation+i+": Error formatting property. Details: "+ 121getExceptionStringRep(ex));}} 122expansion+=childLines.join(","+newLine)+newLine+indentation+"}";return expansion;}else{return formatString(toStr(obj));}} 123return doFormat(obj,depth,indentation);} 124var SimpleDateFormat;(function(){var regex=/('[^']*')|(G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|Z+)|([a-zA-Z]+)|([^a-zA-Z']+)/;var monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];var dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var TEXT2=0,TEXT3=1,NUMBER=2,YEAR=3,MONTH=4,TIMEZONE=5;var types={G:TEXT2,y:YEAR,M:MONTH,w:NUMBER,W:NUMBER,D:NUMBER,d:NUMBER,F:NUMBER,E:TEXT3,a:TEXT2,H:NUMBER,k:NUMBER,K:NUMBER,h:NUMBER,m:NUMBER,s:NUMBER,S:NUMBER,Z:TIMEZONE};var ONE_DAY=24*60*60*1000;var ONE_WEEK=7*ONE_DAY;var DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK=1;var newDateAtMidnight=function(year,month,day){var d=new Date(year,month,day,0,0,0);d.setMilliseconds(0);return d;};Date.prototype.getDifference=function(date){return this.getTime()-date.getTime();};Date.prototype.isBefore=function(d){return this.getTime()<d.getTime();};Date.prototype.getUTCTime=function(){return Date.UTC(this.getFullYear(),this.getMonth(),this.getDate(),this.getHours(),this.getMinutes(),this.get
124Seconds(),this.getMilliseconds());};Date.prototype.getTimeSince=function(d){return this.getUTCTime()-d.getUTCTime();};Date.prototype.getPreviousSunday=function(){var midday=new Date(this.getFullYear(),this.getMonth(),this.getDate(),12,0,0);var previousSunday=new Date(midday.getTime()-this.getDay()*ONE_DAY);return newDateAtMidnight(previousSunday.getFullYear(),previousSunday.getMonth(),previousSunday.getDate());};Date.prototype.getWeekInYear=function(minimalDaysInFirstWeek){if(isUndefined(this.minimalDaysInFirstWeek)){minimalDaysInFirstWeek=DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;} 125var previousSunday=this.getPreviousSunday();var startOfYear=newDateAtMidnight(this.getFullYear(),0,1);var numberOfSundays=previousSunday.isBefore(startOfYear)?0:1+Math.floor(previousSunday.getTimeSince(startOfYear)/ONE_WEEK);var numberOfDaysInFirstWeek=7-startOfYear.getDay();var weekInYear=numberOfSundays;if(numberOfDaysInFirstWeek<minimalDaysInFirstWeek){weekInYear--;} 126return weekInYear;};Date.prototype.getWeekInMonth=function(minimalDaysInFirstWeek){if(isUndefined(this.minimalDaysInFirstWeek)){minimalDaysInFirstWeek=DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;} 127var previousSunday=this.getPreviousSunday();var startOfMonth=newDateAtMidnight(this.getFullYear(),this.getMonth(),1);var numberOfSundays=previousSunday.isBefore(startOfMonth)?0:1+Math.floor(previousSunday.getTimeSince(startOfMonth)/ONE_WEEK);var numberOfDaysInFirstWeek=7-startOfMonth.getDay();var weekInMonth=numberOfSundays;if(numberOfDaysInFirstWeek>=minimalDaysInFirstWeek){weekInMonth++;} 128return weekInMonth;};Date.prototype.getDayInYear=function(){var startOfYear=newDateAtMidnight(this.getFullYear(),0,1);return 1+Math.floor(this.getTimeSince(startOfYear)/ONE_DAY);};SimpleDateFormat=function(formatString){this.formatString=formatString;};SimpleDateFormat.prototype.setMinimalDaysInFirstWeek=function(days){this.minimalDaysInFirstWeek=days;};SimpleDateFormat.prototype.getMinimalDaysInFirstWeek=function(){return isUndefined(this.minimalDaysInFirstWeek)?DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK:this.minimalDaysInFirstWeek;};var padWithZeroes=function(str,len){while(str.length<len){str="0"+str;} 129return str;};var formatText=function(data,numberOfLetters,minLength){return(numberOfLetters>=4)?data:data.substr(0,Math.max(minLength,numberOfLetters));};var formatNumber=function(data,numberOfLetters){var dataString=""+data;return padWithZeroes(dataString,numberOfLetters);};SimpleDateFormat.prototype.format=function(date){var formattedString="";var result;var searchString=this.formatString;while((result=regex.exec(searchString))){var quotedString=result[1];var patternLetters=result[2];var otherLetters=result[3];var otherCharacters=result[4];if(quotedString){if(quotedString=="''"){formattedString+="'";}else{formattedString+=quotedString.substring(1,quotedString.length-1);}}else if(otherLetters){}else if(otherCharacters){formattedString+=otherCharacters;}else if(patternLetters){var patternLetter=patternLetters.charAt(0);var numberOfLetters=patternLetters.length;var rawData="";switch(patternLetter){case"G":rawData="AD";break;case"y":rawData=date.getFullYear();break;case"M":rawData=date.getMonth();break;case"w":rawData=date.getWeekInYear(this.getMinimalDaysInFirstWeek());break;case"W":rawData=date.getWeekInMonth(this.getMinimalDaysInFirstWeek());break;case"D":rawData=date.getDayInYear();break;case"d":rawData=date.getDate();break;case"F":rawData=1+Math.floor((date.getDate()-1)/7);break;case"E":rawData=dayNames[date.getDay()];break;case"a":rawData=(date.getHours()>=12)?"PM":"AM";break;case"H":rawData=date.getHours();break;case"k":rawData=date.getHours()||24;break;case"K":rawData=date.getHours()%12;break;case"h":rawData=(date.getHours()%12)||12;break;case"m":rawData=date.getMinutes();break;case"s":rawData=date.getSeconds();break;case"S":rawData=date.getMilliseconds();break;case"Z":rawData=date.getTimezoneOffset();break;} 130switch(types[patternLetter]){case TEXT2:formattedString+=formatText(rawData,numberOfLetters,2);break;case TEXT3:formattedString+=formatText(rawData,numberOfLetters,3);break;case NUMBER:formattedString+=formatNumber(rawData,numberOfLetters);break;case YEAR:if(numberOfLetters<=3){var dataString=""+rawData;formattedString+=dataString.substr(2,2);}else{formattedString+=formatNumber(rawData,numberOfLetters);} 131break;case MONTH:if(numberOfLetters>=3){formattedString+=formatText(monthNames[rawData],numberOfLetters,numberOfLetters);}else{formattedString+=formatNumber(rawData+1,numberOfLetters);} 132break;case TIMEZONE:var isPositive=(rawData>0);var prefix=isPositive?"-":"+";var absData=Math.abs(rawData);var hours=""+Math.floor(absData/60);hours=padWithZeroes(hours,2);var minutes=""+(absData%60);minutes=padWithZeroes(minutes,2);formattedString+=prefix+hours+minutes;break;}} 133searchString=searchString.substr(result.index+result[0].length);} 134return formattedString;};})();log4javascript.SimpleDateFormat=SimpleDateFormat;function PatternLayout(pattern){if(pattern){this.pattern=pattern;}else{this.pattern=PatternLayout.DEFAULT_CONVERSION_PATTERN;} 135this.customFields=[];} 136PatternLayout.TTCC_CONVERSION_PATTERN="%r %p %c - %m%n";PatternLayout.DEFAULT_CONVERSION_PATTERN="%m%n";PatternLayout.ISO8601_DATEFORMAT="yyyy-MM-dd HH:mm:ss,SSS";PatternLayout.DATETIME_DATEFORMAT="dd MMM yyyy HH:mm:ss,SSS";PatternLayout.ABSOLUTETIME_DATEFORMAT="HH:mm:ss,SSS";PatternLayout.prototype=new Layout();PatternLayout.prototype.format=function(loggingEvent){var regex=/%(-?[0-9]+)?(\.?[0-9]+)?([acdfmMnpr%])(\{([^\}]+)\})?|([^%]+)/;var formattedString="";var result;var searchString=this.pattern;while((result=regex.exec(searchString))){var matchedString=result[0];var padding=result[1];var truncation=result[2];var conversionCharacter=result[3];var specifier=result[5];var text=result[6];if(text){formattedString+=""+text;}else{var replacement="";switch(conversionCharacter){case"a":case"m":var depth=0;
136if(specifier){depth=parseInt(specifier,10);if(isNaN(depth)){handleError("PatternLayout.format: invalid specifier '"+ 137specifier+"' for conversion character '"+conversionCharacter+"' - should be a number");depth=0;}} 138var messages=(conversionCharacter==="a")?loggingEvent.messages[0]:loggingEvent.messages;for(var i=0,len=messages.length;i<len;i++){if(i>0&&(replacement.charAt(replacement.length-1)!==" ")){replacement+=" ";} 139if(depth===0){replacement+=messages[i];}else{replacement+=formatObjectExpansion(messages[i],depth);}} 140break;case"c":var loggerName=loggingEvent.logger.name;if(specifier){var precision=parseInt(specifier,10);var loggerNameBits=loggingEvent.logger.name.split(".");if(precision>=loggerNameBits.length){replacement=loggerName;}else{replacement=loggerNameBits.slice(loggerNameBits.length-precision).join(".");}}else{replacement=loggerName;} 141break;case"d":var dateFormat=PatternLayout.ISO8601_DATEFORMAT;if(specifier){dateFormat=specifier;if(dateFormat=="ISO8601"){dateFormat=PatternLayout.ISO8601_DATEFORMAT;}else if(dateFormat=="ABSOLUTE"){dateFormat=PatternLayout.ABSOLUTETIME_DATEFORMAT;}else if(dateFormat=="DATE"){dateFormat=PatternLayout.DATETIME_DATEFORMAT;}} 142replacement=(new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);break;case"f":if(this.hasCustomFields()){var fieldIndex=0;if(specifier){fieldIndex=parseInt(specifier,10);if(isNaN(fieldIndex)){handleError("PatternLayout.format: invalid specifier '"+ 143specifier+"' for conversion character 'f' - should be a number");}else if(fieldIndex===0){handleError("PatternLayout.format: invalid specifier '"+ 144specifier+"' for conversion character 'f' - must be greater than zero");}else if(fieldIndex>this.customFields.length){handleError("PatternLayout.format: invalid specifier '"+ 145specifier+"' for conversion character 'f' - there aren't that many custom fields");}else{fieldIndex=fieldIndex-1;}} 146replacement=this.customFields[fieldIndex].value;} 147break;case"n":replacement=newLine;break;case"p":replacement=loggingEvent.level.name;break;case"r":replacement=""+loggingEvent.timeStamp.getDifference(applicationStartDate);break;case"%":replacement="%";break;default:replacement=matchedString;break;} 148var l;if(truncation){l=parseInt(truncation.substr(1),10);var strLen=replacement.length;if(l<strLen){replacement=replacement.substring(strLen-l,strLen);}} 149if(padding){if(padding.charAt(0)=="-"){l=parseInt(padding.substr(1),10);while(replacement.length<l){replacement+=" ";}}else{l=parseInt(padding,10);while(replacement.length<l){replacement=" "+replacement;}}} 150formattedString+=replacement;} 151searchString=searchString.substr(result.index+result[0].length);} 152return formattedString;};PatternLayout.prototype.ignoresThrowable=function(){return true;};PatternLayout.prototype.toString=function(){return"PatternLayout";};log4javascript.PatternLayout=PatternLayout;function AlertAppender(){} 153AlertAppender.prototype=new Appender();AlertAppender.prototype.layout=new SimpleLayout();AlertAppender.prototype.append=function(loggingEvent){var formattedMessage=this.getLayout().format(loggingEvent);if(this.getLayout().ignoresThrowable()){formattedMessage+=loggingEvent.getThrowableStrRep();} 154alert(formattedMessage);};AlertAppender.prototype.toString=function(){return"AlertAppender";};log4javascript.AlertAppender=AlertAppender;function BrowserConsoleAppender(){} 155BrowserConsoleAppender.prototype=new log4javascript.Appender();BrowserConsoleAppender.prototype.layout=new NullLayout();BrowserConsoleAppender.prototype.threshold=Level.DEBUG;BrowserConsoleAppender.prototype.append=function(loggingEvent){var appender=this;var getFormattedMessage=function(){var layout=appender.getLayout();var formattedMessage=layout.format(loggingEvent);if(layout.ignoresThrowable()&&loggingEvent.exception){formattedMessage+=loggingEvent.getThrowableStrRep();} 156return formattedMessage;};if((typeof opera!="undefined")&&opera.postError){opera.postError(getFormattedMessage());}else if(window.console&&window.console.log){var formattedMesage=getFormattedMessage();
156if(window.console.debug&&Level.DEBUG.isGreaterOrEqual(loggingEvent.level)){window.console.debug(formattedMesage);}else if(window.console.info&&Level.INFO.equals(loggingEvent.level)){window.console.info(formattedMesage);}else if(window.console.warn&&Level.WARN.equals(loggingEvent.level)){window.console.warn(formattedMesage);}else if(window.console.error&&loggingEvent.level.isGreaterOrEqual(Level.ERROR)){window.console.error(formattedMesage);}else{window.console.log(formattedMesage);}}};BrowserConsoleAppender.prototype.group=function(name){if(window.console&&window.console.group){window.console.group(name);}};BrowserConsoleAppender.prototype.groupEnd=function(){if(window.console&&window.console.groupEnd){window.console.groupEnd();}};BrowserConsoleAppender.prototype.toString=function(){return"BrowserConsoleAppender";};log4javascript.BrowserConsoleAppender=BrowserConsoleAppender;function getXmlHttp(errorHandler){var xmlHttp=null;if(typeof XMLHttpRequest=="object"||typeof XMLHttpRequest=="function"){xmlHttp=new XMLHttpRequest();}else{try{xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");}catch(e1){try{xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");}catch(e2){if(errorHandler){errorHandler();}else{handleError("getXmlHttp: unable to obtain XMLHttpRequest object");}}}} 157return xmlHttp;} 158function isHttpRequestSuccessful(xmlHttp){return(isUndefined(xmlHttp.status)||xmlHttp.status===0||(xmlHttp.status>=200&&xmlHttp.status<300));} 159function AjaxAppender(url){var appender=this;var isSupported=true;if(!url){handleError("AjaxAppender: URL must be specified in constructor");isSupported=false;} 160var timed=this.defaults.timed;var waitForResponse=this.defaults.waitForResponse;var batchSize=this.defaults.batchSize;var timerInterval=this.defaults.timerInterval;var requestSuccessCallback=this.defaults.requestSuccessCallback;var failCallback=this.defaults.failCallback;var postVarName=this.defaults.postVarName;var sendAllOnUnload=this.defaults.sendAllOnUnload;var sessionId=null;var queuedLoggingEvents=[];var queuedRequests=[];var sending=false;var initialized=false;function checkCanConfigure(configOptionName){if(initialized){handleError("AjaxAppender: configuration option '"+ 161configOptionName+"' may not be set after the appender has been initialized");return false;} 162return true;} 163this.getSessionId=function(){return sessionId;};this.setSessionId=function(sessionIdParam){sessionId=extractStringFromParam(sessionIdParam,null);this.layout.setCustomField("sessionid",sessionId);};this.setLayout=function(layoutParam){if(checkCanConfigure("layout")){this.layout=layoutParam;if(sessionId!==null){this.setSessionId(sessionId);}}};this.isTimed=function(){return timed;};this.setTimed=function(timedParam){if(checkCanConfigure("timed")){timed=bool(timedParam);}};this.getTimerInterval=function(){return timerInterval;};this.setTimerInterval=function(timerIntervalParam){if(checkCanConfigure("timerInterval")){timerInterval=extractIntFromParam(timerIntervalParam,timerInterval);}};this.isWaitForResponse=function(){return waitForResponse;};this.setWaitForResponse=function(waitForResponseParam){if(checkCanConfigure("waitForResponse")){waitForResponse=bool(waitForResponseParam);}};this.getBatchSize=function(){return batchSize;};this.setBatchSize=function(batchSizeParam){if(checkCanConfigure("batchSize")){batchSize=extractIntFromParam(batchSizeParam,batchSize);}};this.isSendAllOnUnload=function(){return sendAllOnUnload;};this.setSendAllOnUnload=function(sendAllOnUnloadParam){if(checkCanConfigure("sendAllOnUnload")){sendAllOnUnload=extractIntFromParam(sendAllOnUnloadParam,sendAllOnUnload);}};this.setRequestSuccessCallback=function(requestSuccessCallbackParam){requestSuccessCallback=extractFunctionFromParam(requestSuccessCallbackParam,requestSuccessCallback);};this.setFailCallback=function(failCallbackParam){failCallback=extractFunctionFromParam(failCallbackParam,failCallback);};this.getPostVarName=function(){return postVarName;};this.setPostVarName=function(postVarNameParam){if(checkCanConfigure("postVarName")){postVarName=extractStringFromParam(postVarNameParam,postVarName);}};function sendAll(){if(isSupported&&enabled){sending=true;var currentRequestBatch;if(waitForResponse){if(queuedRequests.length>0){currentRequestBatch=queuedRequests.shift();sendRequest(preparePostData(currentRequestBatch),sendAll);}else{sending=false;if(timed){scheduleSending();}}}else{while((currentRequestBatch=queuedRequests.shift())){sendRequest(preparePostData(currentRequestBatch));} 164sending=false;if(timed){scheduleSending();}}}} 165this.sendAll=sendAll;function sendAllRemaining(){if(isSupported&&enabled){var actualBatchSize=appender.getLayout().allowBatching()?batchSize:1;var currentLoggingEvent;var postData="";var batchedLoggingEvents=[];while((currentLoggingEvent=queuedLoggingEvents.shift())){batchedLoggingEvents.push(currentLoggingEvent);if(queuedLoggingEvents.length>=actualBatchSize){queuedRequests.push(batchedLoggingEvents);batchedLoggingEvents=[];}} 166if(batchedLoggingEvents.length>0){queuedRequests.push(batchedLoggingEvents);} 167waitForResponse=false;timed=false;sendAll();}} 168function preparePostData(batchedLoggingEvents){var formattedMessages=[];var currentLoggingEvent;var postData="";while((currentLoggingEvent=batchedLoggingEvents.shift())){var currentFormattedMessage=appender.getLayout().format(currentLoggingEvent);if(appender.getLayout().ignoresThrowable()){currentFormattedMessage+=loggingEvent.getThrowableStrRep();} 169formattedMessages.push(currentFormattedMessage);} 170if(batchedLoggingEvents.length==1){postData=formattedMessages.join("");}else{postData=appender.getLayout().batchHeader+ 171formattedMessages.join(appender.getLayout().batchSeparator)+ 172appender.getLayout().batchFooter;} 173postData=appender.getLayout().returnsPostData?postData:urlEncode(postVarName)+"="+urlEncode(postData);if(postData.length>0){postData+="&";} 174return postData+"layout="+urlEncode(appender.getLayout().toString());} 175function scheduleSending(){setTimeout(sendAll,timerInterval);} 176function xmlHttpErrorHandler(){var msg="AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";handleError(msg);isSupported=false;
176if(failCallback){failCallback(msg);}} 177function sendRequest(postData,successCallback){try{var xmlHttp=getXmlHttp(xmlHttpErrorHandler);if(isSupported){if(xmlHttp.overrideMimeType){xmlHttp.overrideMimeType(appender.getLayout().getContentType());} 178xmlHttp.onreadystatechange=function(){if(xmlHttp.readyState==4){if(isHttpRequestSuccessful(xmlHttp)){if(requestSuccessCallback){requestSuccessCallback(xmlHttp);} 179if(successCallback){successCallback(xmlHttp);}}else{var msg="AjaxAppender.append: XMLHttpRequest request to URL "+ 180url+" returned status code "+xmlHttp.status;handleError(msg);if(failCallback){failCallback(msg);}} 181xmlHttp.onreadystatechange=emptyFunction;xmlHttp=null;}};xmlHttp.open("POST",url,true);try{xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");}catch(headerEx){var msg="AjaxAppender.append: your browser's XMLHttpRequest implementation"+" does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";handleError(msg);isSupported=false;if(failCallback){failCallback(msg);} 182return;} 183xmlHttp.send(postData);}}catch(ex){var errMsg="AjaxAppender.append: error sending log message to "+url;handleError(errMsg,ex);isSupported=false;if(failCallback){failCallback(errMsg+". Details: "+getExceptionStringRep(ex));}}} 184this.append=function(loggingEvent){if(isSupported){if(!initialized){init();} 185queuedLoggingEvents.push(loggingEvent);var actualBatchSize=this.getLayout().allowBatching()?batchSize:1;if(queuedLoggingEvents.length>=actualBatchSize){var currentLoggingEvent;var postData="";var batchedLoggingEvents=[];while((currentLoggingEvent=queuedLoggingEvents.shift())){batchedLoggingEvents.push(currentLoggingEvent);} 186queuedRequests.push(batchedLoggingEvents);if(!timed){if(!waitForResponse||(waitForResponse&&!sending)){sendAll();}}}}};function init(){initialized=true;if(sendAllOnUnload){addEvent(window,"unload",sendAllRemaining);} 187if(timed){scheduleSending();}}} 188AjaxAppender.prototype=new Appender();AjaxAppender.prototype.defaults={waitForResponse:false,timed:false,timerInterval:1000,batchSize:1,sendAllOnUnload:true,requestSuccessCallback:null,failCallback:null,postVarName:"data"};AjaxAppender.prototype.layout=new HttpPostDataLayout();AjaxAppender.prototype.toString=function(){return"AjaxAppender";};log4javascript.AjaxAppender=AjaxAppender;function setCookie(name,value,days,path){var expires;path=path?"; path="+path:"";if(days){var date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));expires="; expires="+date.toGMTString();}else{expires="";} 189document.cookie=escape(name)+"="+escape(value)+expires+path;} 190function getCookie(name){var nameEquals=escape(name)+"=";var ca=document.cookie.split(";");for(var i=0,len=ca.length;i<len;i++){var c=ca[i];while(c.charAt(0)===" "){c=c.substring(1,c.length);} 191if(c.indexOf(nameEquals)===0){return unescape(c.substring(nameEquals.length,c.length));}} 192return null;} 193function getBaseUrl(){var scripts=document.getElementsByTagName("script");for(var i=0,len=scripts.length;i<len;++i){if(scripts[i].src.indexOf("log4javascript")!=-1){var lastSlash=scripts[i].src.lastIndexOf("/");return(lastSlash==-1)?"":scripts[i].src.substr(0,lastSlash+1);}} 194return null;} 195function isLoaded(win){try{return bool(win.loaded);}catch(ex){return false;}} 196var ConsoleAppender;(function(){var getConsoleHtmlLines=function(){return['<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">','<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">','<head>','<title>log4javascript</title>','<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />','<!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->','<meta http-equiv="X-UA-Compatible" content="IE=7" />','<script type="text/javascript">var isIe = false, isIePre7 = false;</script>','<!--[if IE]><script type="text/javascript">isIe = true</script><![endif]-->','<!--[if lt IE 7]><script type="text/javascript">isIePre7 = true</script><![endif]-->','<script type="text/javascript">','//<![CDATA[','var loggingEnabled=true;var logQueuedEventsTimer=null;var logEntries=[];var logEntriesAndSeparators=[];var logItems=[];var renderDelay=100;var unrenderedLogItemsExist=false;var rootGroup,currentGroup=null;var loaded=false;var currentLogItem=null;var logMainContainer;function copyProperties(obj,props){for(var i in props){obj[i]=props[i];}}','function LogItem(){}','LogItem.prototype={mainContainer:null,wrappedContainer:null,unwrappedContainer:null,group:null,appendToLog:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].appendToLog();}','this.group.update();},doRemove:function(doUpdate,removeFromGroup){if(this.rendered){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].remove();}','this.unwrappedElementContainer=null;this.wrappedElementContainer=null;this.mainElementContainer=null;}','if(this.group&&removeFromGroup){this.group.removeChild(this,doUpdate);}','if(this===currentLogItem){currentLogItem=null;}},remove:function(doUpdate,removeFromGroup){this.doRemove(doUpdate,removeFromGroup);},render:function(){},accept:function(visitor){visitor.visit(this);},getUnwrappedDomContainer:function(){return this.group.unwrappedElementContainer.contentDiv;},getWrappedDomContainer:function(){return this.group.wrappedElementContainer.contentDiv;},getMainDomContainer:function(){return this.group.mainElementContainer.contentDiv;}};LogItem.serializedItemKeys={LOG_ENTRY:0,GROUP_START:1,GROUP_END:2};function LogItemContainerElement(){}','LogItemContainerElement.prototype={appendToLog:function(){var insertBeforeFirst=(newestAtTop&&this.containerDomNode.hasChildNodes());if(insertBeforeFirst){this.containerDomNode.insertBefore(this.mainDiv,this.containerDomNode.firstChild);}else{this.containerDomNode.appendChild(this.mainDiv);}}};function SeparatorElementContainer(containerDomNode){this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="separator";this.mainDiv.innerHTML=" ";}','SeparatorElementContainer.prototype=new LogItemContainerElement();SeparatorElementContainer.prototype.remove=function(){this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;};function Separator(){this.rendered=false;}','Separator.prototype=new LogItem();copyProperties(Separator.prototype,{render:function(){var containerDomNode=this.group.contentDiv;if(isIe){this.unwrappedElementContainer=new SeparatorElementContainer(this.getUnwrappedDomContainer());this.wrappedElementContainer=new SeparatorElementContainer(this.getWrappedDomContainer());this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{this.mainElementContainer=new SeparatorElementContainer(this.getMainDomContainer());this.elementContainers=[this.mainElementContainer];}','this.content=this.formattedMessage;this.rendered=true;}});function GroupElementContainer(group,containerDomNode,isRoot,isWrapped){this.group=group;this.containerDomNode=containerDomNode;this.isRoot=isRoot;this.isWrapped=isWrapped;this.expandable=false;if(this.isRoot){if(isIe){this.contentDiv=logMainContainer.appendChild(document.createElement("div"));this.contentDiv.id=this.isWrapped?"log_wrapped":"log_unwrapped";}else{this.contentDiv=logMainContainer;}}else{var groupElementContainer=this;this.mainDiv=document.createElement("div");this.mainDiv.className="group";this.headingDiv=this.mainDiv.appendChild(document.createElement("div"));this.headingDiv.className="groupheading";this.expander=this.headingDiv.appendChild(document.createElement("span"));this.expander.className="expander unselectable greyedout";this.expander.unselectable=true;var expanderText=this.group.expanded?"-":"+";this.expanderTextNode=this.expander.appendChild(document.createTextNode(expanderText));this.headingDiv.appendChild(document.createTextNode(" "+this.group.name));this.contentDiv=this.mainDiv.appendChild(document.createElement("div"));var contentCssClass=this.group.expanded?"expanded":"collapsed";this.contentDiv.className="groupcontent "+contentCssClass;this.expander.onclick=function(){if(groupElementContainer.group.expandable){groupElementContainer.group.toggleExpanded();}};}}','GroupElementContainer.prototype=new LogItemContainerElement();copyProperties(GroupElementContainer.prototype,{toggleExpanded:function(){if(!this.isRoot){var oldCssClass,newCssClass,exp
196anderText;if(this.group.expanded){newCssClass="expanded";oldCssClass="collapsed";expanderText="-";}else{newCssClass="collapsed";oldCssClass="expanded";expanderText="+";}','replaceClass(this.contentDiv,newCssClass,oldCssClass);this.expanderTextNode.nodeValue=expanderText;}},remove:function(){if(!this.isRoot){this.headingDiv=null;this.expander.onclick=null;this.expander=null;this.expanderTextNode=null;this.contentDiv=null;this.containerDomNode=null;this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;}},reverseChildren:function(){var node=null;var childDomNodes=[];while((node=this.contentDiv.firstChild)){this.contentDiv.removeChild(node);childDomNodes.push(node);}','while((node=childDomNodes.pop())){this.contentDiv.appendChild(node);}},update:function(){if(!this.isRoot){if(this.group.expandable){removeClass(this.expander,"greyedout");}else{addClass(this.expander,"greyedout");}}},clear:function(){if(this.isRoot){this.contentDiv.innerHTML="";}}});function Group(name,isRoot,initiallyExpanded){this.name=name;this.group=null;this.isRoot=isRoot;this.initiallyExpanded=initiallyExpanded;this.elementContainers=[];this.children=[];this.expanded=initiallyExpanded;this.rendered=false;this.expandable=false;}','Group.prototype=new LogItem();copyProperties(Group.prototype,{addChild:function(logItem){this.children.push(logItem);logItem.group=this;},render:function(){if(isIe){var unwrappedDomContainer,wrappedDomContainer;if(this.isRoot){unwrappedDomContainer=logMainContainer;wrappedDomContainer=logMainContainer;}else{unwrappedDomContainer=this.getUnwrappedDomContainer();wrappedDomContainer=this.getWrappedDomContainer();}','this.unwrappedElementContainer=new GroupElementContainer(this,unwrappedDomContainer,this.isRoot,false);this.wrappedElementContainer=new GroupElementContainer(this,wrappedDomContainer,this.isRoot,true);this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{var mainDomContainer=this.isRoot?logMainContainer:this.getMainDomContainer();this.mainElementContainer=new GroupElementContainer(this,mainDomContainer,this.isRoot,false);this.elementContainers=[this.mainElementContainer];}','this.rendered=true;},toggleExpanded:function(){this.expanded=!this.expanded;for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].toggleExpanded();}},expand:function(){if(!this.expanded){this.toggleExpanded();}},accept:function(visitor){visitor.visitGroup(this);},reverseChildren:function(){if(this.rendered){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].reverseChildren();}}},update:function(){var previouslyExpandable=this.expandable;this.expandable=(this.children.length!==0);if(this.expandable!==previouslyExpandable){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].update();}}},flatten:function(){var visitor=new GroupFlattener();this.accept(visitor);return visitor.logEntriesAndSeparators;},removeChild:function(child,doUpdate){array_remove(this.children,child);child.group=null;if(doUpdate){this.update();}},remove:function(doUpdate,removeFromGroup){for(var i=0,len=this.children.length;i<len;i++){this.children[i].remove(false,false);}','this.children=[];this.update();if(this===currentGroup){currentGroup=this.group;}','this.doRemove(doUpdate,removeFromGroup);},serialize:function(items){items.push([LogItem.serializedItemKeys.GROUP_START,this.name]);for(var i=0,len=this.children.length;i<len;i++){this.children[i].serialize(items);}','if(this!==currentGroup){items.push([LogItem.serializedItemKeys.GROUP_END]);}},clear:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].clear();}}});function LogEntryElementContainer(){}','LogEntryElementContainer.prototype=new LogItemContainerElement();copyProperties(LogEntryElementContainer.prototype,{remove:function(){this.doRemove();},doRemove:function(){this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;this.contentElement=null;this.containerDomNode=null;},setContent:function(content,wrappedContent){if(content===this.formattedMessage){this.contentElement.innerHTML="";this.contentElement.appendChild(document.createTextNode(this.formattedMessage));}else{this.contentElement.innerHTML=content;}},setSearchMatch:function(isMatch){var oldCssClass=isMatch?"searchnonmatch":"searchmatch";var newCssClass=isMatch?"searchmatch":"searchnonmatch";replaceClass(this.mainDiv,newCssClass,oldCssClass);},clearSearch:function(){removeClass(this.mainDiv,"searchmatch");removeClass(this.mainDiv,"searchnonmatch");}});
196function LogEntryWrappedElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.appendChild(document.createTextNode(this.logEntry.formattedMessage));this.mainDiv.className="logentry wrapped "+this.logEntry.level;this.contentElement=this.mainDiv;}','LogEntryWrappedElementContainer.prototype=new LogEntryElementContainer();LogEntryWrappedElementContainer.prototype.setContent=function(content,wrappedContent){if(content===this.formattedMessage){this.contentElement.innerHTML="";this.contentElement.appendChild(document.createTextNode(this.formattedMessage));}else{this.contentElement.innerHTML=wrappedContent;}};function LogEntryUnwrappedElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="logentry unwrapped "+this.logEntry.level;this.pre=this.mainDiv.appendChild(document.createElement("pre"));this.pre.appendChild(document.createTextNode(this.logEntry.formattedMessage));this.pre.className="unwrapped";this.contentElement=this.pre;}','LogEntryUnwrappedElementContainer.prototype=new LogEntryElementContainer();LogEntryUnwrappedElementContainer.prototype.remove=function(){this.doRemove();this.pre=null;};function LogEntryMainElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="logentry nonielogentry "+this.logEntry.level;this.contentElement=this.mainDiv.appendChild(document.createElement("span"));this.contentElement.appendChild(document.createTextNode(this.logEntry.formattedMessage));}','LogEntryMainElementContainer.prototype=new LogEntryElementContainer();function LogEntry(level,formattedMessage){this.level=level;this.formattedMessage=formattedMessage;this.rendered=false;}','LogEntry.prototype=new LogItem();copyProperties(LogEntry.prototype,{render:function(){var logEntry=this;var containerDomNode=this.group.contentDiv;if(isIe){this.formattedMessage=this.formattedMessage.replace(/\\r\\n/g,"\\r");this.unwrappedElementContainer=new LogEntryUnwrappedElementContainer(this,this.getUnwrappedDomContainer());this.wrappedElementContainer=new LogEntryWrappedElementContainer(this,this.getWrappedDomContainer());this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{this.mainElementContainer=new LogEntryMainElementContainer(this,this.getMainDomContainer());this.elementContainers=[this.mainElementContainer];}','this.content=this.formattedMessage;this.rendered=true;},setContent:function(content,wrappedContent){if(content!=this.content){if(isIe&&(content!==this.formattedMessage)){content=content.replace(/\\r\\n/g,"\\r");}','for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].setContent(content,wrappedContent);}','this.content=content;}},getSearchMatches:function(){var matches=[];var i,len;if(isIe){var unwrappedEls=getElementsByClass(this.unwrappedElementContainer.mainDiv,"searchterm","span");var wrappedEls=getElementsByClass(this.wrappedElementContainer.mainDiv,"searchterm","span");for(i=0,len=unwrappedEls.length;i<len;i++){matches[i]=new Match(this.level,null,unwrappedEls[i],wrappedEls[i]);}}else{var els=getElementsByClass(this.mainElementContainer.mainDiv,"searchterm","span");for(i=0,len=els.length;i<len;i++){matches[i]=new Match(this.level,els[i]);}}','return matches;},setSearchMatch:function(isMatch){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].setSearchMatch(isMatch);}},clearSearch:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].clearSearch();}},accept:function(visitor){visitor.visitLogEntry(this);},serialize:function(items){items.push([LogItem.serializedItemKeys.LOG_ENTRY,this.level,this.formattedMessage]);}});function LogItemVisitor(){}','LogItemVisitor.prototype={visit:function(logItem){},visitParent:function(logItem){if(logItem.group){logItem.group.accept(this);}},visitChildren:function(logItem){for(var i=0,len=logItem.children.length;i<len;i++){logItem.children[i].accept(this);}}
196,visitLogEntry:function(logEntry){this.visit(logEntry);},visitSeparator:function(separator){this.visit(separator);},visitGroup:function(group){this.visit(group);}};function GroupFlattener(){this.logEntriesAndSeparators=[];}','GroupFlattener.prototype=new LogItemVisitor();GroupFlattener.prototype.visitGroup=function(group){this.visitChildren(group);};GroupFlattener.prototype.visitLogEntry=function(logEntry){this.logEntriesAndSeparators.push(logEntry);};GroupFlattener.prototype.visitSeparator=function(separator){this.logEntriesAndSeparators.push(separator);};window.onload=function(){if(location.search){var queryBits=unescape(location.search).substr(1).split("&"),nameValueBits;for(var i=0,len=queryBits.length;i<len;i++){nameValueBits=queryBits[i].split("=");if(nameValueBits[0]=="log4javascript_domain"){document.domain=nameValueBits[1];break;}}}','logMainContainer=$("log");if(isIePre7){addClass(logMainContainer,"oldIe");}','rootGroup=new Group("root",true);rootGroup.render();currentGroup=rootGroup;setCommandInputWidth();setLogContainerHeight();toggleLoggingEnabled();toggleSearchEnabled();toggleSearchFilter();toggleSearchHighlight();applyFilters();checkAllLevels();toggleWrap();toggleNewestAtTop();toggleScrollToLatest();renderQueuedLogItems();loaded=true;$("command").value="";$("command").autocomplete="off";$("command").onkeydown=function(evt){evt=getEvent(evt);if(evt.keyCode==10||evt.keyCode==13){evalCommandLine();stopPropagation(evt);}else if(evt.keyCode==27){this.value="";this.focus();}else if(evt.keyCode==38&&commandHistory.length>0){currentCommandIndex=Math.max(0,currentCommandIndex-1);this.value=commandHistory[currentCommandIndex];moveCaretToEnd(this);}else if(evt.keyCode==40&&commandHistory.length>0){currentCommandIndex=Math.min(commandHistory.length-1,currentCommandIndex+1);this.value=commandHistory[currentCommandIndex];moveCaretToEnd(this);}};$("command").onkeypress=function(evt){evt=getEvent(evt);if(evt.keyCode==38&&commandHistory.length>0&&evt.preventDefault){evt.preventDefault();}};$("command").onkeyup=function(evt){evt=getEvent(evt);if(evt.keyCode==27&&evt.preventDefault){evt.preventDefault();this.focus();}};document.onkeydown=function keyEventHandler(evt){evt=getEvent(evt);switch(evt.keyCode){case 69:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){evalLastCommand();cancelKeyEvent(evt);return false;}','break;case 75:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){focusSearch();cancelKeyEvent(evt);return false;}','break;case 40:case 76:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){focusCommandLine();cancelKeyEvent(evt);return false;}','break;}};setTimeout(setLogContainerHeight,20);setShowCommandLine(showCommandLine);doSearch();};window.onunload=function(){if(mainWindowExists()){appender.unload();}','appender=null;};function toggleLoggingEnabled(){setLoggingEnabled($("enableLogging").checked);}','function setLoggingEnabled(enable){loggingEnabled=enable;}','var appender=null;function setAppender(appenderParam){appender=appenderParam;}','function setShowCloseButton(showCloseButton){$("closeButton").style.display=showCloseButton?"inline":"none";}','function setShowHideButton(showHideButton){$("hideButton").style.display=showHideButton?"inline":"none";}','var newestAtTop=false;function LogItemContentReverser(){}','LogItemContentReverser.prototype=new LogItemVisitor();LogItemContentReverser.prototype.visitGroup=function(group){group.reverseChildren();this.visitChildren(group);};function setNewestAtTop(isNewestAtTop){var oldNewestAtTop=newestAtTop;var i,iLen,j,jLen;newestAtTop=Boolean(isNewestAtTop);if(oldNewestAtTop!=newestAtTop){var visitor=new LogItemContentReverser();rootGroup.accept(visitor);if(currentSearch){var currentMatch=currentSearch.matches[currentMatchIndex];var matchIndex=0;var matches=[];var actOnLogEntry=function(logEntry){var logEntryMatches=logEntry.getSearchMatches();for(j=0,jLen=logEntryMatches.length;j<jLen;j++){matches[matchIndex]=logEntryMatches[j];if(currentMatch&&logEntryMatches[j].equals(currentMatch)){currentMatchIndex=matchIndex;}','matchIndex++;}};if(newestAtTop){for(i=logEntries.length-1;i>=0;i--){actOnLogEntry(logEntries[i]);}}
196else{for(i=0,iLen=logEntries.length;i<iLen;i++){actOnLogEntry(logEntries[i]);}}','currentSearch.matches=matches;if(currentMatch){currentMatch.setCurrent();}}else if(scrollToLatest){doScrollToLatest();}}','$("newestAtTop").checked=isNewestAtTop;}','function toggleNewestAtTop(){var isNewestAtTop=$("newestAtTop").checked;setNewestAtTop(isNewestAtTop);}','var scrollToLatest=true;function setScrollToLatest(isScrollToLatest){scrollToLatest=isScrollToLatest;if(scrollToLatest){doScrollToLatest();}','$("scrollToLatest").checked=isScrollToLatest;}','function toggleScrollToLatest(){var isScrollToLatest=$("scrollToLatest").checked;setScrollToLatest(isScrollToLatest);}','function doScrollToLatest(){var l=logMainContainer;if(typeof l.scrollTop!="undefined"){if(newestAtTop){l.scrollTop=0;}else{var latestLogEntry=l.lastChild;if(latestLogEntry){l.scrollTop=l.scrollHeight;}}}}','var closeIfOpenerCloses=true;function setCloseIfOpenerCloses(isCloseIfOpenerCloses){closeIfOpenerCloses=isCloseIfOpenerCloses;}','var maxMessages=null;function setMaxMessages(max){maxMessages=max;pruneLogEntries();}','var showCommandLine=false;function setShowCommandLine(isShowCommandLine){showCommandLine=isShowCommandLine;if(loaded){$("commandLine").style.display=showCommandLine?"block":"none";setCommandInputWidth();setLogContainerHeight();}}','function focusCommandLine(){if(loaded){$("command").focus();}}','function focusSearch(){if(loaded){$("searchBox").focus();}}','function getLogItems(){var items=[];for(var i=0,len=logItems.length;i<len;i++){logItems[i].serialize(items);}','return items;}','function setLogItems(items){var loggingReallyEnabled=loggingEnabled;loggingEnabled=true;for(var i=0,len=items.length;i<len;i++){switch(items[i][0]){case LogItem.serializedItemKeys.LOG_ENTRY:log(items[i][1],items[i][2]);break;case LogItem.serializedItemKeys.GROUP_START:group(items[i][1]);break;case LogItem.serializedItemKeys.GROUP_END:groupEnd();break;}}','loggingEnabled=loggingReallyEnabled;}','function log(logLevel,formattedMessage){if(loggingEnabled){var logEntry=new LogEntry(logLevel,formattedMessage);logEntries.push(logEntry);logEntriesAndSeparators.push(logEntry);logItems.push(logEntry);currentGroup.addChild(logEntry);if(loaded){if(logQueuedEventsTimer!==null){clearTimeout(logQueuedEventsTimer);}','logQueuedEventsTimer=setTimeout(renderQueuedLogItems,renderDelay);unrenderedLogItemsExist=true;}}}','function renderQueuedLogItems(){logQueuedEventsTimer=null;var pruned=pruneLogEntries();var initiallyHasMatches=currentSearch?currentSearch.hasMatches():false;for(var i=0,len=logItems.length;i<len;i++){if(!logItems[i].rendered){logItems[i].render();logItems[i].appendToLog();if(currentSearch&&(logItems[i]instanceof LogEntry)){currentSearch.applyTo(logItems[i]);}}}','if(currentSearch){if(pruned){if(currentSearch.hasVisibleMatches()){if(currentMatchIndex===null){setCurrentMatchIndex(0);}','displayMatches();}else{displayNoMatches();}}else if(!initiallyHasMatches&¤tSearch.hasVisibleMatches()){setCurrentMatchIndex(0);displayMatches();}}','if(scrollToLatest){doScrollToLatest();}','unrenderedLogItemsExist=false;}','function pruneLogEntries(){if((maxMessages!==null)&&(logEntriesAndSeparators.length>maxMessages)){var numberToDelete=logEntriesAndSeparators.length-maxMessages;var prunedLogEntries=logEntriesAndSeparators.slice(0,numberToDelete);if(currentSearch){currentSearch.removeMatches(prunedLogEntries);}','var group;for(var i=0;i<numberToDelete;i++){group=logEntriesAndSeparators[i].group;array_remove(logItems,logEntriesAndSeparators[i]);array_remove(logEntries,logEntriesAndSeparators[i]);logEntriesAndSeparators[i].remove(true,true);if(group.children.length===0&&group!==currentGroup&&group!==rootGroup){array_remove(logItems,group);group.remove(true,true);}}','logEntriesAndSeparators=array_removeFromStart(logEntriesAndSeparators,numberToDelete);return true;}','return false;}','function group(name,startExpanded){if(loggingEnabled){initiallyExpanded=(typeof startExpanded==="undefined")?true:Boolean(startExpanded);var newGroup=new Group(name,false,initiallyExpanded);currentGroup.addChild(newGroup);currentGroup=newGroup;logItems.push(newGroup);if(loaded){if(logQueuedEventsTimer!==null){clearTimeout(logQueuedEventsTimer);}','logQueuedEventsTimer=setTimeout(renderQueuedLogItems,renderDelay);unrenderedLogItemsExist=true;}}}','function groupEnd(){currentGroup=(currentGroup===rootGroup)?rootGroup:currentGroup.group;}','function mainPageReloaded(){currentGroup=rootGroup;var separator=new Separator();logEntriesAndSeparators.push(separator);logItems.push(separator);currentGroup.addChild(separator);}','function closeWindow(){if(appender&&mainWindowExists()){appender.close(true);}else{window.close();}}
196','function hide(){if(appender&&mainWindowExists()){appender.hide();}}','var mainWindow=window;var windowId="log4javascriptConsoleWindow_"+new Date().getTime()+"_"+(""+Math.random()).substr(2);function setMainWindow(win){mainWindow=win;mainWindow[windowId]=window;if(opener&&closeIfOpenerCloses){pollOpener();}}','function pollOpener(){if(closeIfOpenerCloses){if(mainWindowExists()){setTimeout(pollOpener,500);}else{closeWindow();}}}','function mainWindowExists(){try{return(mainWindow&&!mainWindow.closed&&mainWindow[windowId]==window);}catch(ex){}','return false;}','var logLevels=["TRACE","DEBUG","INFO","WARN","ERROR","FATAL"];function getCheckBox(logLevel){return $("switch_"+logLevel);}','function getIeWrappedLogContainer(){return $("log_wrapped");}','function getIeUnwrappedLogContainer(){return $("log_unwrapped");}','function applyFilters(){for(var i=0;i<logLevels.length;i++){if(getCheckBox(logLevels[i]).checked){addClass(logMainContainer,logLevels[i]);}else{removeClass(logMainContainer,logLevels[i]);}}','updateSearchFromFilters();}','function toggleAllLevels(){var turnOn=$("switch_ALL").checked;for(var i=0;i<logLevels.length;i++){getCheckBox(logLevels[i]).checked=turnOn;if(turnOn){addClass(logMainContainer,logLevels[i]);}else{removeClass(logMainContainer,logLevels[i]);}}}','function checkAllLevels(){for(var i=0;i<logLevels.length;i++){if(!getCheckBox(logLevels[i]).checked){getCheckBox("ALL").checked=false;return;}}','getCheckBox("ALL").checked=true;}','function clearLog(){rootGroup.clear();currentGroup=rootGroup;logEntries=[];logItems=[];logEntriesAndSeparators=[];doSearch();}','function toggleWrap(){var enable=$("wrap").checked;if(enable){addClass(logMainContainer,"wrap");}else{removeClass(logMainContainer,"wrap");}','refreshCurrentMatch();}','var searchTimer=null;function scheduleSearch(){try{clearTimeout(searchTimer);}catch(ex){}','searchTimer=setTimeout(doSearch,500);}','function Search(searchTerm,isRegex,searchRegex,isCaseSensitive){this.searchTerm=searchTerm;this.isRegex=isRegex;this.searchRegex=searchRegex;this.isCaseSensitive=isCaseSensitive;this.matches=[];}','Search.prototype={hasMatches:function(){return this.matches.length>0;},hasVisibleMatches:function(){if(this.hasMatches()){for(var i=0;i<this.matches.length;i++){if(this.matches[i].isVisible()){return true;}}}','return false;},match:function(logEntry){var entryText=String(logEntry.formattedMessage);var matchesSearch=false;if(this.isRegex){matchesSearch=this.searchRegex.test(entryText);}else if(this.isCaseSensitive){matchesSearch=(entryText.indexOf(this.searchTerm)>-1);}else{matchesSearch=(entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase())>-1);}','return matchesSearch;},getNextVisibleMatchIndex:function(){for(var i=currentMatchIndex+1;i<this.matches.length;i++){if(this.matches[i].isVisible()){return i;}}','for(i=0;i<=currentMatchIndex;i++){if(this.matches[i].isVisible()){return i;}}','return-1;},getPreviousVisibleMatchIndex:function(){for(var i=currentMatchIndex-1;i>=0;i--){if(this.matches[i].isVisible()){return i;}}','for(var i=this.matches.length-1;i>=currentMatchIndex;i--){if(this.matches[i].isVisible()){return i;}}','return-1;},applyTo:function(logEntry){var doesMatch=this.match(logEntry);if(doesMatch){logEntry.group.expand();logEntry.setSearchMatch(true);var logEntryContent;var wrappedLogEntryContent;var searchTermReplacementStartTag="<span class=\\\"searchterm\\\">";var searchTermReplacementEndTag="<"+"/span>";var preTagName=isIe?"pre":"span";var preStartTag="<"+preTagName+" class=\\\"pre\\\">";var preEndTag="<"+"/"+preTagName+">";var startIndex=0;var searchIndex,matchedText,textBeforeMatch;if(this.isRegex){var flags=this.isCaseSensitive?"g":"gi";var capturingRegex=new RegExp("("+this.searchRegex.source+")",flags);var rnd=(""+Math.random()).substr(2);var startToken="%%s"+rnd+"%%";var endToken="%%e"+rnd+"%%";logEntryContent=logEntry.formattedMessage.replace(capturingRegex,startToken+"$1"+endToken);logEntryContent=escapeHtml(logEntryContent);var result;var searchString=logEntryContent;logEntryContent="";wrappedLogEntryContent="";while((searchIndex=searchString.indexOf(startToken,startIndex))>-1){var endTokenIndex=searchString.indexOf(endToken,searchIndex);matchedText=searchString.substring(searchIndex+startToken.length,endTokenIndex);textBeforeMatch=searchString.substring(startIndex,searchIndex);logEntryContent+=preStartTag+textBeforeMatch+preEndTag;logEntryContent+=searchTermReplacementStartTag+preStartTag+matchedText+','preEndTag+searchTermReplacementEndTag;if(isIe){wrappedLogEntryContent+=textBeforeMatch+searchTermReplacementStartTag+','matchedText+searchTermReplacementEndTag;}','startIndex=endTokenIndex+endToken.length;}','logEntryContent+=preStartTag+searchString.substr(startIndex)+preEndTag;if(isIe){wrappedLogEntryContent+=searchString.substr(startIndex);}}else{logEntryContent="";wrappedLogEntryContent="";var searchTermReplacementLength=searchTermReplacementStartTag.length+','this.searchTerm.length+searchTermReplacementEndTag.length;var searchTermLength=this.searchTerm.length;var searchTermLowerCase=this.searchTerm.toLowerCase();var logTextLowerCase=logEntry.formattedMessage.toLowerCase();while((searchIndex=logTextLowerCase.indexOf(searchTermLowerCase,startIndex))>-1){matchedText=escapeHtml(logEntry.formattedMessage.substr(searchIndex,this.searchTerm.length));textBeforeMatch=escapeHtml(logEntry.formattedMessage.substring(startIndex,searchIndex));var searchTermReplacement=searchTermReplacementStartTag+','preStartTag+matchedText+preEndTag+searchTermReplacementEndTag;logEntryContent+=preStartTag+textBeforeMatch+preEndTag+searchTermReplacement;if(isIe){wrappedLogEntryContent+=textBeforeMatch+searchTermReplacementStartTag+','matchedText+searchTermReplacementEndTag;}','startIndex=searchIndex+searchTermLength;}','var textAfterLastMatch=escapeHtml(logEntry.formattedMessage.substr(startIndex));logEntryContent+=preStartTag+textAfterLastMatch+preEndTag;if(isIe){wrappedLogEntryContent+=textAfterLastMatch;}}','logEntry.setContent(logEntryContent,wrappedLogEntryContent);var logEntryMatches=logEntry.getSearchMatches();this.matches=this.matches.concat(logEntryMatches);}else{logEntry.setSearchMatch(false);logEntry.setContent(logEntry.formattedMessage,logEntry.formattedMessage);}','return doesMatch;},removeMatches:function(logEntries){var matchesToRemoveCount=0;var currentMatchRemoved=false;var matchesToRemove=[];var i,iLen,j,jLen;for(i=0,iLen=this.matches.length;i<iLen;i++){for(j=0,jLen=logEntries.length;j<jLen;j++){if(this.matches[i].belongsTo(logEntries[j])){matchesToRemove.push(this.matches[i]);if(i===currentMatchIndex){currentMatchRemoved=true;}}}}','var newMatch=currentMatchRemoved?null:this.matches[currentMatchIndex];if(currentMatchRemoved){for(i=currentMatchIndex,iLen=this.matches.length;i<iLen;i++){if(this.matches[i].isVisible()&&!array_contains(matchesToRemove,this.matches[i])){newMatch=this.matches[i];break;}}}','for(i=0,iLen=matchesToRemove.length;i<iLen;i++){array_remove(this.matches,matchesToRemove[i]);matchesToRemove[i].remove();}
196','if(this.hasVisibleMatches()){if(newMatch===null){setCurrentMatchIndex(0);}else{var newMatchIndex=0;for(i=0,iLen=this.matches.length;i<iLen;i++){if(newMatch===this.matches[i]){newMatchIndex=i;break;}}','setCurrentMatchIndex(newMatchIndex);}}else{currentMatchIndex=null;displayNoMatches();}}};function getPageOffsetTop(el,container){var currentEl=el;var y=0;while(currentEl&¤tEl!=container){y+=currentEl.offsetTop;currentEl=currentEl.offsetParent;}','return y;}','function scrollIntoView(el){var logContainer=logMainContainer;if(!$("wrap").checked){var logContainerLeft=logContainer.scrollLeft;var logContainerRight=logContainerLeft+logContainer.offsetWidth;var elLeft=el.offsetLeft;var elRight=elLeft+el.offsetWidth;if(elLeft<logContainerLeft||elRight>logContainerRight){logContainer.scrollLeft=elLeft-(logContainer.offsetWidth-el.offsetWidth)/2;}}','var logContainerTop=logContainer.scrollTop;var logContainerBottom=logContainerTop+logContainer.offsetHeight;var elTop=getPageOffsetTop(el)-getToolBarsHeight();var elBottom=elTop+el.offsetHeight;if(elTop<logContainerTop||elBottom>logContainerBottom){logContainer.scrollTop=elTop-(logContainer.offsetHeight-el.offsetHeight)/2;}}','function Match(logEntryLevel,spanInMainDiv,spanInUnwrappedPre,spanInWrappedDiv){this.logEntryLevel=logEntryLevel;this.spanInMainDiv=spanInMainDiv;if(isIe){this.spanInUnwrappedPre=spanInUnwrappedPre;this.spanInWrappedDiv=spanInWrappedDiv;}','this.mainSpan=isIe?spanInUnwrappedPre:spanInMainDiv;}','Match.prototype={equals:function(match){return this.mainSpan===match.mainSpan;},setCurrent:function(){if(isIe){addClass(this.spanInUnwrappedPre,"currentmatch");addClass(this.spanInWrappedDiv,"currentmatch");var elementToScroll=$("wrap").checked?this.spanInWrappedDiv:this.spanInUnwrappedPre;scrollIntoView(elementToScroll);}else{addClass(this.spanInMainDiv,"currentmatch");scrollIntoView(this.spanInMainDiv);}},belongsTo:function(logEntry){if(isIe){return isDescendant(this.spanInUnwrappedPre,logEntry.unwrappedPre);}else{return isDescendant(this.spanInMainDiv,logEntry.mainDiv);}},setNotCurrent:function(){if(isIe){removeClass(this.spanInUnwrappedPre,"currentmatch");removeClass(this.spanInWrappedDiv,"currentmatch");}else{removeClass(this.spanInMainDiv,"currentmatch");}},isOrphan:function(){return isOrphan(this.mainSpan);},isVisible:function(){return getCheckBox(this.logEntryLevel).checked;},remove:function(){if(isIe){this.spanInUnwrappedPre=null;this.spanInWrappedDiv=null;}else{this.spanInMainDiv=null;}}};var currentSearch=null;var currentMatchIndex=null;function doSearch(){var searchBox=$("searchBox");var searchTerm=searchBox.value;var isRegex=$("searchRegex").checked;var isCaseSensitive=$("searchCaseSensitive").checked;var i;if(searchTerm===""){$("searchReset").disabled=true;$("searchNav").style.display="none";removeClass(document.body,"searching");removeClass(searchBox,"hasmatches");removeClass(searchBox,"nomatches");for(i=0;i<logEntries.length;i++){logEntries[i].clearSearch();logEntries[i].setContent(logEntries[i].formattedMessage,logEntries[i].formattedMessage);}','currentSearch=null;setLogContainerHeight();}else{$("searchReset").disabled=false;$("searchNav").style.display="block";var searchRegex;var regexValid;if(isRegex){try{searchRegex=isCaseSensitive?new RegExp(searchTerm,"g"):new RegExp(searchTerm,"gi");regexValid=true;replaceClass(searchBox,"validregex","invalidregex");searchBox.title="Valid regex";}catch(ex){regexValid=false;replaceClass(searchBox,"invalidregex","validregex");searchBox.title="Invalid regex: "+(ex.message?ex.message:(ex.description?ex.description:"unknown error"));return;}}else{searchBox.title="";removeClass(searchBox,"validregex");removeClass(searchBox,"invalidregex");}','addClass(document.body,"searching");currentSearch=new Search(searchTerm,isRegex,searchRegex,isCaseSensitive);for(i=0;i<logEntries.length;i++){currentSearch.applyTo(logEntries[i]);}','setLogContainerHeight();if(currentSearch.hasVisibleMatches()){setCurrentMatchIndex(0);displayMatches();}else{displayNoMatches();}}}','function updateSearchFromFilters(){if(currentSearch){if(currentSearch.hasMatches()){if(currentMatchIndex===null){currentMatchIndex=0;}','var currentMatch=currentSearch.matches[currentMatchIndex];if(currentMatch.isVisible()){displayMatches();setCurrentMatchIndex(currentMatchIndex);}else{currentMatch.setNotCurrent();var nextVisibleMatchIndex=currentSearch.getNextVisibleMatchIndex();if(nextVisibleMatchIndex>-1){setCurrentMatchIndex(nextVisibleMatchIndex);displayMatches();}else{displayNoMatches();}}}else{displayNoMatches();}}}','function refreshCurrentMatch(){if(currentSearch&¤tSearch.hasVisibleMatches()){setCurrentMatchIndex(currentMatchIndex);}}','function displayMatches(){replaceClass($("searchBox"),"hasmatches","nomatches");$("searchBox").title=""+currentSearch.matches.length+" matches found";$("searchNav").style.display="block";
196setLogContainerHeight();}','function displayNoMatches(){replaceClass($("searchBox"),"nomatches","hasmatches");$("searchBox").title="No matches found";$("searchNav").style.display="none";setLogContainerHeight();}','function toggleSearchEnabled(enable){enable=(typeof enable=="undefined")?!$("searchDisable").checked:enable;$("searchBox").disabled=!enable;$("searchReset").disabled=!enable;$("searchRegex").disabled=!enable;$("searchNext").disabled=!enable;$("searchPrevious").disabled=!enable;$("searchCaseSensitive").disabled=!enable;$("searchNav").style.display=(enable&&($("searchBox").value!=="")&¤tSearch&¤tSearch.hasVisibleMatches())?"block":"none";if(enable){removeClass($("search"),"greyedout");addClass(document.body,"searching");if($("searchHighlight").checked){addClass(logMainContainer,"searchhighlight");}else{removeClass(logMainContainer,"searchhighlight");}','if($("searchFilter").checked){addClass(logMainContainer,"searchfilter");}else{removeClass(logMainContainer,"searchfilter");}','$("searchDisable").checked=!enable;}else{addClass($("search"),"greyedout");removeClass(document.body,"searching");removeClass(logMainContainer,"searchhighlight");removeClass(logMainContainer,"searchfilter");}','setLogContainerHeight();}','function toggleSearchFilter(){var enable=$("searchFilter").checked;if(enable){addClass(logMainContainer,"searchfilter");}else{removeClass(logMainContainer,"searchfilter");}','refreshCurrentMatch();}','function toggleSearchHighlight(){var enable=$("searchHighlight").checked;if(enable){addClass(logMainContainer,"searchhighlight");}else{removeClass(logMainContainer,"searchhighlight");}}','function clearSearch(){$("searchBox").value="";doSearch();}','function searchNext(){if(currentSearch!==null&¤tMatchIndex!==null){currentSearch.matches[currentMatchIndex].setNotCurrent();var nextMatchIndex=currentSearch.getNextVisibleMatchIndex();if(nextMatchIndex>currentMatchIndex||confirm("Reached the end of the page. Start from the top?")){setCurrentMatchIndex(nextMatchIndex);}}}','function searchPrevious(){if(currentSearch!==null&¤tMatchIndex!==null){currentSearch.matches[currentMatchIndex].setNotCurrent();var previousMatchIndex=currentSearch.getPreviousVisibleMatchIndex();if(previousMatchIndex<currentMatchIndex||confirm("Reached the start of the page. Continue from the bottom?")){setCurrentMatchIndex(previousMatchIndex);}}}','function setCurrentMatchIndex(index){currentMatchIndex=index;currentSearch.matches[currentMatchIndex].setCurrent();}','function addClass(el,cssClass){if(!hasClass(el,cssClass)){if(el.className){el.className+=" "+cssClass;}else{el.className=cssClass;}}}','function hasClass(el,cssClass){if(el.className){var classNames=el.className.split(" ");return array_contains(classNames,cssClass);}','return false;}','function removeClass(el,cssClass){if(hasClass(el,cssClass)){var existingClasses=el.className.split(" ");var newClasses=[];for(var i=0,len=existingClasses.length;i<len;i++){if(existingClasses[i]!=cssClass){newClasses[newClasses.length]=existingClasses[i];}}','el.className=newClasses.join(" ");}}','function replaceClass(el,newCssClass,oldCssClass){removeClass(el,oldCssClass);addClass(el,newCssClass);}','function getElementsByClass(el,cssClass,tagName){var elements=el.getElementsByTagName(tagName);var matches=[];for(var i=0,len=elements.length;i<len;i++){if(hasClass(elements[i],cssClass)){matches.push(elements[i]);}}','return matches;}','function $(id){return document.getElementById(id);}','function isDescendant(node,ancestorNode){while(node!=null){if(node===ancestorNode){return true;}','node=node.parentNode;}','return false;}','function isOrphan(node){var currentNode=node;while(currentNode){if(currentNode==document.body){return false;}','currentNode=currentNode.parentNode;}','return true;}','function escapeHtml(str){return str.replace(/&/g,"&").replace(/[<]/g,"<").replace(/>/g,">");}','function getWindowWidth(){if(window.innerWidth){return window.innerWidth;}else if(document.documentElement&&document.documentElement.clientWidth){return document.documentElement.clientWidth;}else if(document.body){return document.body.clientWidth;}','return 0;}','function getWindowHeight(){if(window.innerHeight){return window.innerHeight;}else if(document.documentElement&&document.documentElement.clientHeight){return document.documentElement.clientHeight;}else if(document.body){return document.body.clientHeight;}','return 0;}','function getToolBarsHeight(){return $("switches").offsetHeight;}','function getChromeHeight(){var height=getToolBarsHeight();if(showCommandLine){height+=$("commandLine").offsetHeight;}','return height;}','function setLogContainerHeight(){if(logMainContainer){var windowHeight=getWindowHeight();$("body").style.height=getWindowHeight()+"px";logMainContainer.style.height=""+','Math.max(0,windowHeight-getChromeHeight())+"px";}}','function setCommandInputWidth(){if(showCommandLine){$("command").style.width=""+Math.max(0,$("commandLineContainer").offsetWidth-','($("evaluateButton").offsetWidth+13))+"px";}}','window.onresize=function(){setCommandInputWidth();setLogContainerHeight();};if(!Array.prototype.push){Array.prototype.push=function(){for(var i=0,len=arguments.length;i<len;i++){this[this.length]=arguments[i];}','return this.length;};}','if(!Array.prototype.pop){Array.prototype.pop=function(){if(this.length>0){var val=this[this.length-1];this.length=this.length-1;return val;}};}','if(!Array.prototype.shift){Array.prototype.shift=function(){if(this.length>0){var firstItem=this[0];for(var i=0,len=this.length-1;i<len;i++){this[i]=this[i+1];}','this.length=this.length-1;return firstItem;}};}','if(!Array.prototype.splice){Array.prototype.splice=function(startIndex,deleteCount){var itemsAfterDeleted=this.slice(startIndex+deleteCount);var itemsDeleted=this.slice(startIndex,startIndex+deleteCount);this.length=startIndex;var argumentsArray=[];for(var i=0,len=arguments.length;i<len;i++){argumentsArray[i]=arguments[i];}','var itemsToAppend=(argumentsArray.length>2)?itemsAfterDeleted=argumentsArray.slice(2).concat(itemsAfterDeleted):itemsAfterDeleted;for(i=0,len=itemsToAppend.length;i<len;i++){this.push(itemsToAppend[i]);}','return itemsDeleted;};}','function array_remove(arr,val){var index=-1;for(var i=0,len=arr.length;i<len;i++){if(arr[i]===val){index=i;break;}}','if(index>=0){arr.splice(index,1);return index;}else{return false;}}','function array_removeFromStart(array,numberToRemove){if(Array.prototype.splice){array.splice(0,numberToRemove);}else{for(var i=numberToRemove,len=array.length;i<len;i++){array[i-numberToRemove]=array[i];}','array.length=array.length-numberToRemove;}','return array;}','function array_contains(arr,val){for(var i=0,len=arr.length;i<len;i++){if(arr[i]==val){return true;}}','return false;}','function getErrorMessage(ex){if(ex.message){return ex.message;}else if(ex.description){return ex.description;}','return""+ex;}','function moveCaretToEnd(input){if(input.setSelectionRange){input.focus();var length=input.value.length;input.setSelectionRange(length,length);}else if(input.createTextRange){var range=input.createTextRange();range.collapse(false);range.select();}
196','input.focus();}','function stopPropagation(evt){if(evt.stopPropagation){evt.stopPropagation();}else if(typeof evt.cancelBubble!="undefined"){evt.cancelBubble=true;}}','function getEvent(evt){return evt?evt:event;}','function getTarget(evt){return evt.target?evt.target:evt.srcElement;}','function getRelatedTarget(evt){if(evt.relatedTarget){return evt.relatedTarget;}else if(evt.srcElement){switch(evt.type){case"mouseover":return evt.fromElement;case"mouseout":return evt.toElement;default:return evt.srcElement;}}}','function cancelKeyEvent(evt){evt.returnValue=false;stopPropagation(evt);}','function evalCommandLine(){var expr=$("command").value;evalCommand(expr);$("command").value="";}','function evalLastCommand(){if(lastCommand!=null){evalCommand(lastCommand);}}','var lastCommand=null;var commandHistory=[];var currentCommandIndex=0;function evalCommand(expr){if(appender){appender.evalCommandAndAppend(expr);}else{var prefix=">>> "+expr+"\\r\\n";try{log("INFO",prefix+eval(expr));}catch(ex){log("ERROR",prefix+"Error: "+getErrorMessage(ex));}}','if(expr!=commandHistory[commandHistory.length-1]){commandHistory.push(expr);if(appender){appender.storeCommandHistory(commandHistory);}}','currentCommandIndex=(expr==commandHistory[currentCommandIndex])?currentCommandIndex+1:commandHistory.length;lastCommand=expr;}','//]]>','</script>','<style type="text/css">','body{background-color:white;color:black;padding:0;margin:0;font-family:tahoma,verdana,arial,helvetica,sans-serif;overflow:hidden}div#switchesContainer input{margin-bottom:0}div.toolbar{border-top:solid #ffffff 1px;border-bottom:solid #aca899 1px;background-color:#f1efe7;padding:3px 5px;font-size:68.75%}div.toolbar,div#search input{font-family:tahoma,verdana,arial,helvetica,sans-serif}div.toolbar input.button{padding:0 5px;font-size:100%}div.toolbar input.hidden{display:none}div#switches input#clearButton{margin-left:20px}div#levels label{font-weight:bold}div#levels label,div#options label{margin-right:5px}div#levels label#wrapLabel{font-weight:normal}div#search label{margin-right:10px}div#search label.searchboxlabel{margin-right:0}div#search input{font-size:100%}div#search input.validregex{color:green}div#search input.invalidregex{color:red}div#search input.nomatches{color:white;background-color:#ff6666}div#search input.nomatches{color:white;background-color:#ff6666}div#searchNav{display:none}div#commandLine{display:none}div#commandLine input#command{font-size:100%;font-family:Courier New,Courier}div#commandLine input#evaluateButton{}*.greyedout{color:gray !important;border-color:gray !important}*.greyedout *.alwaysenabled{color:black}*.unselectable{-khtml-user-select:none;-moz-user-select:none;user-select:none}div#log{font-family:Courier New,Courier;font-size:75%;width:100%;overflow:auto;clear:both;position:relative}div.group{border-color:#cccccc;border-style:solid;border-width:1px 0 1px 1px;overflow:visible}div.oldIe div.group,div.oldIe div.group *,div.oldIe *.logentry{height:1%}div.group div.groupheading span.expander{border:solid black 1px;font-family:Courier New,Courier;font-size:0.833em;background-color:#eeeeee;position:relative;top:-1px;color:black;padding:0 2px;cursor:pointer;cursor:hand;height:1%}div.group div.groupcontent{margin-left:10px;padding-bottom:2px;overflow:visible}div.group div.expanded{display:block}div.group div.collapsed{display:none}*.logentry{overflow:visible;display:none;white-space:pre}span.pre{white-space:pre}pre.unwrapped{display:inline !important}pre.unwrapped pre.pre,div.wrapped pre.pre{display:inline}div.wrapped pre.pre{white-space:normal}div.wrapped{display:none}body.searching *.logentry span.currentmatch{color:white !important;background-color:green !important}body.searching div.searchhighlight *.logentry span.searchterm{color:black;background-color:yellow}div.wrap *.logentry{white-space:normal !important;border-width:0 0 1px 0;border-color:#dddddd;border-style:dotted}div.wrap #log_wrapped,#log_unwrapped{display:block}div.wrap #log_unwrapped,#log_wrapped{display:none}div.wrap *.logentry span.pre{overflow:visible;white-space:normal}div.wrap *.logentry pre.unwrapped{display:none}div.wrap *.logentry span.wrapped{display:inline}div.searchfilter *.searchnonmatch{display:none !important}div#log *.TRACE,label#label_TRACE{color:#666666}div#log *.DEBUG,label#label_DEBUG{color:green}div#log *.INFO,label#label_INFO{color:#000099}div#log *.WARN,label#label_WARN{color:#999900}div#log *.ERROR,label#label_ERROR{color:red}div#log *.FATAL,label#label_FATAL{color:#660066}div.TRACE#log *.TRACE,div.DEBUG#log *.DEBUG,div.INFO#log *.INFO,div.WARN#log *.WARN,div.ERROR#log *.ERROR,div.FATAL#log *.FATAL{display:block}div#log div.separator{background-color:#cccccc;margin:5px 0;line-height:1px}','</style>','</head>','<body id="body">','<div id="switchesContainer">','<div id="switches">','<div id="levels" class="toolbar">','Filters:','<input type="checkbox" id="switch_TRACE" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide trace messages" /><label for="switch_TRACE" id="label_TRACE">trace</label>','<input type="checkbox" id="switch_DEBUG" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide debug messages" /><label for="switch_DEBUG" id="label_DEBUG">debug</label>','<input type="checkbox" id="switch_INFO" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide info messages" /><label for="switch_INFO" id="label_INFO">info</label>','<input type="checkbox" id="switch_WARN" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide warn messages" /><label for="switch_WARN" id="label_WARN">warn</label>','<input type="checkbox" id="switch_ERROR" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide error messages" /><label for="switch_ERROR" id="label_ERROR">error</label>','<input type="checkbox" id="switch_FATAL" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide fatal messages" /><label for="switch_FATAL" id="label_FATAL">fatal</label>','<input type="checkbox" id="switch_ALL" onclick="toggleAllLevels(); applyFilters()" checked="checked" title="Show/hide all messages" /><label for="switch_ALL" id="label_ALL">all</label>','</div>','<div id="search" class="toolbar">','<label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />','<input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />','<input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>','<input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>','<input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>','<div id="searchNav">','<input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext(
196)" class="button" title="Go to the next matching log entry" />','<input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />','<input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>','<input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>','</div>','</div>','<div id="options" class="toolbar">','Options:','<input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Log</label>','<input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>','<input type="checkbox" id="newestAtTop" onclick="toggleNewestAtTop()" title="If checked, causes newest messages to appear at the top" /><label for="newestAtTop" id="newestAtTopLabel">Newest at the top</label>','<input type="checkbox" id="scrollToLatest" onclick="toggleScrollToLatest()" checked="checked" title="If checked, window automatically scrolls to a new message when it is added" /><label for="scrollToLatest" id="scrollToLatestLabel">Scroll to latest</label>','<input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages" />','<input type="button" id="hideButton" value="Hide" onclick="hide()" class="hidden button" title="Hide the console" />','<input type="button" id="closeButton" value="Close" onclick="closeWindow()" class="hidden button" title="Close the window" />','</div>','</div>','</div>','<div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>','<div id="commandLine" class="toolbar">','<div id="commandLineContainer">','<input type="text" id="command" title="Enter a JavaScript command here and hit return or press \'Evaluate\'" />','<input type="button" id="evaluateButton" value="Evaluate" class="button" title="Evaluate the command" onclick="evalCommandLine()" />','</div>','</div>','</body>','</html>',''];};var defaultCommandLineFunctions=[];ConsoleAppender=function(){};
196var consoleAppenderIdCounter=1;ConsoleAppender.prototype=new Appender();ConsoleAppender.prototype.create=function(inPage,container,lazyInit,initiallyMinimized,useDocumentWrite,width,height,focusConsoleWindow){var appender=this;var initialized=false;var consoleWindowCreated=false;var consoleWindowLoaded=false;var consoleClosed=false;var queuedLoggingEvents=[];var isSupported=true;var consoleAppenderId=consoleAppenderIdCounter++;initiallyMinimized=extractBooleanFromParam(initiallyMinimized,this.defaults.initiallyMinimized);lazyInit=extractBooleanFromParam(lazyInit,this.defaults.lazyInit);useDocumentWrite=extractBooleanFromParam(useDocumentWrite,this.defaults.useDocumentWrite);var newestMessageAtTop=this.defaults.newestMessageAtTop;var scrollToLatestMessage=this.defaults.scrollToLatestMessage;width=width?width:this.defaults.width;height=height?height:this.defaults.height;var maxMessages=this.defaults.maxMessages;var showCommandLine=this.defaults.showCommandLine;var commandLineObjectExpansionDepth=this.defaults.commandLineObjectExpansionDepth;var showHideButton=this.defaults.showHideButton;var showCloseButton=this.defaults.showCloseButton;var showLogEntryDeleteButtons=this.defaults.showLogEntryDeleteButtons;this.setLayout(this.defaults.layout);var init,createWindow,safeToAppend,getConsoleWindow,open;var appenderName=inPage?"InPageAppender":"PopUpAppender";var checkCanConfigure=function(configOptionName){if(consoleWindowCreated){handleError(appenderName+": configuration option '"+configOptionName+"' may not be set after the appender has been initialized");return false;} 197return true;};var consoleWindowExists=function(){return(consoleWindowLoaded&&isSupported&&!consoleClosed);};this.isNewestMessageAtTop=function(){return newestMessageAtTop;};this.setNewestMessageAtTop=function(newestMessageAtTopParam){newestMessageAtTop=bool(newestMessageAtTopParam);if(consoleWindowExists()){getConsoleWindow().setNewestAtTop(newestMessageAtTop);}};this.isScrollToLatestMessage=function(){return scrollToLatestMessage;};this.setScrollToLatestMessage=function(scrollToLatestMessageParam){scrollToLatestMessage=bool(scrollToLatestMessageParam);if(consoleWindowExists()){getConsoleWindow().setScrollToLatest(scrollToLatestMessage);}};this.getWidth=function(){return width;};this.setWidth=function(widthParam){if(checkCanConfigure("width")){width=extractStringFromParam(widthParam,width);}};this.getHeight=function(){return height;};this.setHeight=function(heightParam){if(checkCanConfigure("height")){height=extractStringFromParam(heightParam,height);}};this.getMaxMessages=function(){return maxMessages;};this.setMaxMessages=function(maxMessagesParam){maxMessages=extractIntFromParam(maxMessagesParam,maxMessages);if(consoleWindowExists()){getConsoleWindow().setMaxMessages(maxMessages);}};this.isShowCommandLine=function(){return showCommandLine;};this.setShowCommandLine=function(showCommandLineParam){showCommandLine=bool(showCommandLineParam);if(consoleWindowExists()){getConsoleWindow().setShowCommandLine(showCommandLine);}};this.isShowHideButton=function(){return showHideButton;};this.setShowHideButton=function(showHideButtonParam){showHideButton=bool(showHideButtonParam);if(consoleWindowExists()){getConsoleWindow().setShowHideButton(showHideButton);}};this.isShowCloseButton=function(){return showCloseButton;};this.setShowCloseButton=function(showCloseButtonParam){showCloseButton=bool(showCloseButtonParam);if(consoleWindowExists()){getConsoleWindow().setShowCloseButton(showCloseButton);}};this.getCommandLineObjectExpansionDepth=function(){return commandLineObjectExpansionDepth;};this.setCommandLineObjectExpansionDepth=function(commandLineObjectExpansionDepthParam){commandLineObjectExpansionDepth=extractIntFromParam(commandLineObjectExpansionDepthParam,commandLineObjectExpansionDepth);};var minimized=initiallyMinimized;this.isInitiallyMinimized=function(){return initiallyMinimized;};this.setInitiallyMinimized=function(initiallyMinimizedParam){if(checkCanConfigure("initiallyMinimized")){initiallyMinimized=bool(initiallyMinimizedParam);minimized=initiallyMinimized;}};this.isUseDocumentWrite=function(){return useDocumentWrite;};this.setUseDocumentWrite=function(useDocumentWriteParam){if(checkCanConfigure("useDocumentWrite")){useDocumentWrite=bool(useDocumentWriteParam);}};function QueuedLoggingEvent(loggingEvent,formattedMessage){this.loggingEvent=loggingEvent;this.levelName=loggingEvent.level.name;this.formattedMessage=formattedMessage;} 198QueuedLoggingEvent.prototype.append=function(){getConsoleWindow().log(this.levelName,this.formattedMessage);};function QueuedGroup(name,initiallyExpanded){this.name=name;this.initiallyExpanded=initiallyExpanded;} 199QueuedGroup.prototype.append=function(){getConsoleWindow().group(this.name,this.initiallyExpanded);};function QueuedGroupEnd(){} 200QueuedGroupEnd.prototype.append=function(){getConsoleWindow().groupEnd();};var checkAndAppend=function(){safeToAppend();
200if(!initialized){init();}else if(consoleClosed&&reopenWhenClosed){createWindow();} 201if(safeToAppend()){appendQueuedLoggingEvents();}};this.append=function(loggingEvent){if(isSupported){var formattedMessage=appender.getLayout().format(loggingEvent);if(this.getLayout().ignoresThrowable()){formattedMessage+=loggingEvent.getThrowableStrRep();} 202queuedLoggingEvents.push(new QueuedLoggingEvent(loggingEvent,formattedMessage));checkAndAppend();}};this.group=function(name,initiallyExpanded){if(isSupported){queuedLoggingEvents.push(new QueuedGroup(name,initiallyExpanded));checkAndAppend();}};this.groupEnd=function(){if(isSupported){queuedLoggingEvents.push(new QueuedGroupEnd());checkAndAppend();}};var appendQueuedLoggingEvents=function(){var currentLoggingEvent;while(queuedLoggingEvents.length>0){queuedLoggingEvents.shift().append();} 203if(focusConsoleWindow){getConsoleWindow().focus();}};this.setAddedToLogger=function(logger){this.loggers.push(logger);if(enabled&&!lazyInit){init();}};this.clear=function(){if(consoleWindowExists()){getConsoleWindow().clearLog();} 204queuedLoggingEvents.length=0;};this.focus=function(){if(consoleWindowExists()){getConsoleWindow().focus();}};this.focusCommandLine=function(){if(consoleWindowExists()){getConsoleWindow().focusCommandLine();}};this.focusSearch=function(){if(consoleWindowExists()){getConsoleWindow().focusSearch();}};var commandWindow=window;this.getCommandWindow=function(){return commandWindow;};this.setCommandWindow=function(commandWindowParam){commandWindow=commandWindowParam;};this.executeLastCommand=function(){if(consoleWindowExists()){getConsoleWindow().evalLastCommand();}};var commandLayout=new PatternLayout("%m");this.getCommandLayout=function(){return commandLayout;};this.setCommandLayout=function(commandLayoutParam){commandLayout=commandLayoutParam;};this.evalCommandAndAppend=function(expr){var commandReturnValue={appendResult:true,isError:false};var commandOutput="";try{var result,i;if(!commandWindow.eval&&commandWindow.execScript){commandWindow.execScript("null");} 205var commandLineFunctionsHash={};for(i=0,len=commandLineFunctions.length;i<len;i++){commandLineFunctionsHash[commandLineFunctions[i][0]]=commandLineFunctions[i][1];} 206var objectsToRestore=[];var addObjectToRestore=function(name){objectsToRestore.push([name,commandWindow[name]]);};addObjectToRestore("appender");commandWindow.appender=appender;addObjectToRestore("commandReturnValue");commandWindow.commandReturnValue=commandReturnValue;addObjectToRestore("commandLineFunctionsHash");commandWindow.commandLineFunctionsHash=commandLineFunctionsHash;var addFunctionToWindow=function(name){addObjectToRestore(name);commandWindow[name]=function(){return this.commandLineFunctionsHash[name](appender,arguments,commandReturnValue);};};for(i=0,len=commandLineFunctions.length;i<len;i++){addFunctionToWindow(commandLineFunctions[i][0]);} 207if(commandWindow===window&&commandWindow.execScript){addObjectToRestore("evalExpr");addObjectToRestore("result");window.evalExpr=expr;commandWindow.execScript("window.result=eval(window.evalExpr);");result=window.result;}
207else{result=commandWindow.eval(expr);} 208commandOutput=isUndefined(result)?result:formatObjectExpansion(result,commandLineObjectExpansionDepth);for(i=0,len=objectsToRestore.length;i<len;i++){commandWindow[objectsToRestore[i][0]]=objectsToRestore[i][1];}}catch(ex){commandOutput="Error evaluating command: "+getExceptionStringRep(ex);commandReturnValue.isError=true;} 209if(commandReturnValue.appendResult){var message=">>> "+expr;if(!isUndefined(commandOutput)){message+=newLine+commandOutput;} 210var level=commandReturnValue.isError?Level.ERROR:Level.INFO;var loggingEvent=new LoggingEvent(null,new Date(),level,[message],null);var mainLayout=this.getLayout();this.setLayout(commandLayout);this.append(loggingEvent);this.setLayout(mainLayout);}};var commandLineFunctions=defaultCommandLineFunctions.concat([]);this.addCommandLineFunction=function(functionName,commandLineFunction){commandLineFunctions.push([functionName,commandLineFunction]);};var commandHistoryCookieName="log4javascriptCommandHistory";this.storeCommandHistory=function(commandHistory){setCookie(commandHistoryCookieName,commandHistory.join(","));};var writeHtml=function(doc){var lines=getConsoleHtmlLines();doc.open();for(var i=0,len=lines.length;i<len;i++){doc.writeln(lines[i]);} 211doc.close();};this.setEventTypes(["load","unload"]);var consoleWindowLoadHandler=function(){var win=getConsoleWindow();win.setAppender(appender);win.setNewestAtTop(newestMessageAtTop);win.setScrollToLatest(scrollToLatestMessage);win.setMaxMessages(maxMessages);win.setShowCommandLine(showCommandLine);win.setShowHideButton(showHideButton);win.setShowCloseButton(showCloseButton);win.setMainWindow(window);var storedValue=getCookie(commandHistoryCookieName);if(storedValue){win.commandHistory=storedValue.split(",");win.currentCommandIndex=win.commandHistory.length;} 212appender.dispatchEvent("load",{"win":win});};this.unload=function(){logLog.debug("unload "+this+", caller: "+this.unload.caller);if(!consoleClosed){logLog.debug("really doing unload "+this);consoleClosed=true;consoleWindowLoaded=false;consoleWindowCreated=false;appender.dispatchEvent("unload",{});}};var pollConsoleWindow=function(windowTest,interval,successCallback,errorMessage){function doPoll(){try{if(consoleClosed){clearInterval(poll);} 213if(windowTest(getConsoleWindow())){clearInterval(poll);successCallback();}}catch(ex){clearInterval(poll);isSupported=false;handleError(errorMessage,ex);}} 214var poll=setInterval(doPoll,interval);};var getConsoleUrl=function(){var documentDomainSet=(document.domain!=location.hostname);return useDocumentWrite?"":getBaseUrl()+"console.html"+ 215(documentDomainSet?"?log4javascript_domain="+escape(document.domain):"");};if(inPage){var containerElement=null;var cssProperties=[];this.addCssProperty=function(name,value){if(checkCanConfigure("cssProperties")){cssProperties.push([name,value]);}};var windowCreationStarted=false;var iframeContainerDiv;var iframeId=uniqueId+"_InPageAppender_"+consoleAppenderId;this.hide=function(){if(initialized&&consoleWindowCreated){if(consoleWindowExists()){getConsoleWindow().$("command").blur();} 216iframeContainerDiv.style.display="none";minimized=true;}};this.show=function(){if(initialized){if(consoleWindowCreated){iframeContainerDiv.style.display="block";this.setShowCommandLine(showCommandLine);minimized=false;}else if(!windowCreationStarted){createWindow(true);}}};this.isVisible=function(){return!minimized&&!consoleClosed;};this.close=function(fromButton){if(!consoleClosed&&(!fromButton||confirm("This will permanently remove the console from the page. No more messages will be logged. Do you wish to continue?"))){iframeContainerDiv.parentNode.removeChild(iframeContainerDiv);this.unload();}};open=function(){var initErrorMessage="InPageAppender.open: unable to create console iframe";function finalInit(){try{if(!initiallyMinimized){appender.show();} 217consoleWindowLoadHandler();consoleWindowLoaded=true;appendQueuedLoggingEvents();}catch(ex){isSupported=false;handleError(initErrorMessage,ex);}} 218function writeToDocument(){try{var windowTest=function(win){return isLoaded(win);};if(useDocumentWrite){writeHtml(getConsoleWindow().document);} 219if(windowTest(getConsoleWindow())){finalInit();}else{pollConsoleWindow(windowTest,100,finalInit,initErrorMessage);}}catch(ex){isSupported=false;handleError(initErrorMessage,ex);}} 220minimized=false;
220iframeContainerDiv=containerElement.appendChild(document.createElement("div"));iframeContainerDiv.style.width=width;iframeContainerDiv.style.height=height;iframeContainerDiv.style.border="solid gray 1px";for(var i=0,len=cssProperties.length;i<len;i++){iframeContainerDiv.style[cssProperties[i][0]]=cssProperties[i][1];} 221var iframeSrc=useDocumentWrite?"":" src='"+getConsoleUrl()+"'";iframeContainerDiv.innerHTML="<iframe id='"+iframeId+"' name='"+iframeId+"' width='100%' height='100%' frameborder='0'"+iframeSrc+"scrolling='no'></iframe>";consoleClosed=false;var iframeDocumentExistsTest=function(win){try{return bool(win)&&bool(win.document);}catch(ex){return false;}};if(iframeDocumentExistsTest(getConsoleWindow())){writeToDocument();}else{pollConsoleWindow(iframeDocumentExistsTest,100,writeToDocument,initErrorMessage);} 222consoleWindowCreated=true;};createWindow=function(show){if(show||!initiallyMinimized){var pageLoadHandler=function(){if(!container){containerElement=document.createElement("div");containerElement.style.position="fixed";containerElement.style.left="0";containerElement.style.right="0";containerElement.style.bottom="0";document.body.appendChild(containerElement);appender.addCssProperty("borderWidth","1px 0 0 0");appender.addCssProperty("zIndex",1000000);open();}else{try{var el=document.getElementById(container);if(el.nodeType==1){containerElement=el;} 223open();}catch(ex){handleError("InPageAppender.init: invalid container element '"+container+"' supplied",ex);}}};if(pageLoaded&&container&&container.appendChild){containerElement=container;open();}else if(pageLoaded){pageLoadHandler();}else{log4javascript.addEventListener("load",pageLoadHandler);} 224windowCreationStarted=true;}};init=function(){createWindow();initialized=true;};getConsoleWindow=function(){var iframe=window.frames[iframeId];if(iframe){return iframe;}};safeToAppend=function(){if(isSupported&&!consoleClosed){if(consoleWindowCreated&&!consoleWindowLoaded&&getConsoleWindow()&&isLoaded(getConsoleWindow())){consoleWindowLoaded=true;} 225return consoleWindowLoaded;} 226return false;};}else{var useOldPopUp=appender.defaults.useOldPopUp;var complainAboutPopUpBlocking=appender.defaults.complainAboutPopUpBlocking;var reopenWhenClosed=this.defaults.reopenWhenClosed;this.isUseOldPopUp=function(){return useOldPopUp;};this.setUseOldPopUp=function(useOldPopUpParam){if(checkCanConfigure("useOldPopUp")){useOldPopUp=bool(useOldPopUpParam);}};this.isComplainAboutPopUpBlocking=function(){return complainAboutPopUpBlocking;};this.setComplainAboutPopUpBlocking=function(complainAboutPopUpBlockingParam){if(checkCanConfigure("complainAboutPopUpBlocking")){complainAboutPopUpBlocking=bool(complainAboutPopUpBlockingParam);}};this.isFocusPopUp=function(){return focusConsoleWindow;};this.setFocusPopUp=function(focusPopUpParam){focusConsoleWindow=bool(focusPopUpParam);};this.isReopenWhenClosed=function(){return reopenWhenClosed;};this.setReopenWhenClosed=function(reopenWhenClosedParam){reopenWhenClosed=bool(reopenWhenClosedParam);};this.close=function(){logLog.debug("close "+this);try{popUp.close();this.unload();}catch(ex){}};this.hide=function(){logLog.debug("hide "+this);if(consoleWindowExists()){this.close();}};this.show=function(){logLog.debug("show "+this);if(!consoleWindowCreated){open();}};this.isVisible=function(){return safeToAppend();};var popUp;open=function(){var windowProperties="width="+width+",height="+height+",status,resizable";var windowName="PopUp_"+location.host.replace(/[^a-z0-9]/gi,"_")+"_"+consoleAppenderId;if(!useOldPopUp||!useDocumentWrite){windowName=windowName+"_"+uniqueId;} 227var checkPopUpClosed=function(win){if(consoleClosed){return true;}else{try{return bool(win)&&win.closed;}catch(ex){}} 228return false;};var popUpClosedCallback=function(){if(!consoleClosed){appender.unload();}};function finalInit(){getConsoleWindow().setCloseIfOpenerCloses(!useOldPopUp||!useDocumentWrite);consoleWindowLoadHandler();consoleWindowLoaded=true;appendQueuedLoggingEvents();pollConsoleWindow(checkPopUpClosed,500,popUpClosedCallback,"PopUpAppender.checkPopUpClosed: error checking pop-up window");} 229try{popUp=window.open(getConsoleUrl(),windowName,windowProperties);consoleClosed=false;consoleWindowCreated=true;if(popUp){if(useDocumentWrite&&useOldPopUp&&isLoaded(popUp)){popUp.mainPageReloaded();finalInit();}else{if(useDocumentWrite){writeHtml(popUp.document);} 230var popUpLoadedTest=function(win){return bool(win)&&isLoaded(win);};if(isLoaded(popUp)){finalInit();}else{pollConsoleWindow(popUpLoadedTest,100,finalInit,"PopUpAppender.init: unable to create console window");}}}else{isSupported=false;logLog.warn("PopUpAppender.init: pop-ups blocked, please unblock to use PopUpAppender");if(complainAboutPopUpBlocking){handleError("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");}}}catch(ex){handleError("PopUpAppender.init: error creating pop-up",ex);}};createWindow=function(){if(!initiallyMinimized){open();}};init=function(){createWindow();initialized=true;};getConsoleWindow=function(){return popUp;};
230safeToAppend=function(){if(isSupported&&!isUndefined(popUp)&&!consoleClosed){if(popUp.closed||(consoleWindowLoaded&&isUndefined(popUp.closed))){appender.unload();logLog.debug("PopUpAppender: pop-up closed");return false;} 231if(!consoleWindowLoaded&&isLoaded(popUp)){consoleWindowLoaded=true;}} 232return isSupported&&consoleWindowLoaded&&!consoleClosed;};} 233this.getConsoleWindow=getConsoleWindow;};ConsoleAppender.addGlobalCommandLineFunction=function(functionName,commandLineFunction){defaultCommandLineFunctions.push([functionName,commandLineFunction]);};function PopUpAppender(lazyInit,initiallyMinimized,useDocumentWrite,width,height){this.create(false,null,lazyInit,initiallyMinimized,useDocumentWrite,width,height,this.defaults.focusPopUp);} 234PopUpAppender.prototype=new ConsoleAppender();PopUpAppender.prototype.defaults={layout:new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),initiallyMinimized:false,focusPopUp:false,lazyInit:true,useOldPopUp:true,complainAboutPopUpBlocking:true,newestMessageAtTop:false,scrollToLatestMessage:true,width:"600",height:"400",reopenWhenClosed:false,maxMessages:null,showCommandLine:true,commandLineObjectExpansionDepth:1,showHideButton:false,showCloseButton:true,showLogEntryDeleteButtons:true,useDocumentWrite:true};PopUpAppender.prototype.toString=function(){return"PopUpAppender";};log4javascript.PopUpAppender=PopUpAppender;function InPageAppender(container,lazyInit,initiallyMinimized,useDocumentWrite,width,height){this.create(true,container,lazyInit,initiallyMinimized,useDocumentWrite,width,height,false);} 235InPageAppender.prototype=new ConsoleAppender();InPageAppender.prototype.defaults={layout:new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),initiallyMinimized:false,lazyInit:true,newestMessageAtTop:false,scrollToLatestMessage:true,width:"100%",height:"220px",maxMessages:null,showCommandLine:true,commandLineObjectExpansionDepth:1,showHideButton:false,showCloseButton:false,showLogEntryDeleteButtons:true,useDocumentWrite:true};InPageAppender.prototype.toString=function(){return"InPageAppender";};log4javascript.InPageAppender=InPageAppender;log4javascript.InlineAppender=InPageAppender;})();function padWithSpaces(str,len){if(str.length<len){var spaces=[];var numberOfSpaces=Math.max(0,len-str.length);for(var i=0;i<numberOfSpaces;i++){spaces[i]=" ";} 236str+=spaces.join("");} 237return str;} 238(function(){function dir(obj){var maxLen=0;for(var p in obj){maxLen=Math.max(toStr(p).length,maxLen);} 239var propList=[];for(p in obj){var propNameStr=" "+padWithSpaces(toStr(p),maxLen+2);var propVal;try{propVal=splitIntoLines(toStr(obj[p])).join(padWithSpaces(newLine,maxLen+6));}catch(ex){propVal="[Error obtaining property. Details: "+getExceptionMessage(ex)+"]";} 240propList.push(propNameStr+propVal);} 241return propList.join(newLine);} 242var nodeTypes={ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12};var preFormattedElements=["script","pre"];var emptyElements=["br","img","hr","param","link","area","input","col","base","meta"];var indentationUnit=" ";function getXhtml(rootNode,includeRootNode,indentation,startNewLine,preformatted){includeRootNode=(typeof includeRootNode=="undefined")?true:(includeRootNode?true:false);if(typeof indentation!="string"){indentation="";} 243startNewLine=startNewLine?true:false;preformatted=preformatted?true:false;var xhtml;function isWhitespace(node){return((node.nodeType==nodeTypes.TEXT_NODE)&&/^[ \t\r\n]*$/.test(node.nodeValue));} 244function fixAttributeValue(attrValue){return attrValue.toString().replace(/\&/g,"&").replace(/</g,"<").replace(/\"/g,""");} 245function getStyleAttributeValue(el){var stylePairs=el.style.cssText.split(";");var styleValue="";var isFirst=true;for(var j=0,len=stylePairs.length;j<len;j++){var nameValueBits=stylePairs[j].split(":");var props=[];if(!/^\s*$/.test(nameValueBits[0])){props.push(trim(nameValueBits[0]).toLowerCase()+":"+trim(nameValueBits[1]));} 246styleValue=props.join(";");} 247return styleValue;} 248function getNamespace(el){if(el.prefix){return el.prefix;}else if(el.outerHTML){var regex=new RegExp("<([^:]+):"+el.tagName+"[^>]*>","i");if(regex.test(el.outerHTML)){return RegExp.$1.toLowerCase();}} 249return"";} 250var lt="<";var gt=">";if(includeRootNode&&rootNode.nodeType!=nodeTypes.DOCUMENT_FRAGMENT_NODE){switch(rootNode.nodeType){case nodeTypes.ELEMENT_NODE:var tagName=rootNode.tagName.toLowerCase();xhtml=startNewLine?newLine+indentation:"";xhtml+=lt;var prefix=getNamespace(rootNode);var hasPrefix=prefix?true:false;if(hasPrefix){xhtml+=prefix+":";} 251xhtml+=tagName;for(i=0,len=rootNode.attributes.length;i<len;i++){var currentAttr=rootNode.attributes[i];if(!currentAttr.specified||currentAttr.nodeValue===null||currentAttr.nodeName.toLowerCase()==="style"||typeof currentAttr.nodeValue!=="string"||currentAttr.nodeName.indexOf("_moz")===0){continue;} 252xhtml+=" "+currentAttr.nodeName.toLowerCase()+"=\"";xhtml+=fixAttributeValue(currentAttr.nodeValue);xhtml+="\"";} 253if(rootNode.style.cssText){var styleValue=getStyleAttributeValue(rootNode);
253if(styleValue!==""){xhtml+=" style=\""+getStyleAttributeValue(rootNode)+"\"";}} 254if(array_contains(emptyElements,tagName)||(hasPrefix&&!rootNode.hasChildNodes())){xhtml+="/"+gt;}else{xhtml+=gt;var childStartNewLine=!(rootNode.childNodes.length===1&&rootNode.childNodes[0].nodeType===nodeTypes.TEXT_NODE);var childPreformatted=array_contains(preFormattedElements,tagName);for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation+indentationUnit,childStartNewLine,childPreformatted);} 255var endTag=lt+"/"+tagName+gt;xhtml+=childStartNewLine?newLine+indentation+endTag:endTag;} 256return xhtml;case nodeTypes.TEXT_NODE:if(isWhitespace(rootNode)){xhtml="";}else{if(preformatted){xhtml=rootNode.nodeValue;}else{var lines=splitIntoLines(trim(rootNode.nodeValue));var trimmedLines=[];for(var i=0,len=lines.length;i<len;i++){trimmedLines[i]=trim(lines[i]);} 257xhtml=trimmedLines.join(newLine+indentation);} 258if(startNewLine){xhtml=newLine+indentation+xhtml;}} 259return xhtml;case nodeTypes.CDATA_SECTION_NODE:return"<![CDA"+"TA["+rootNode.nodeValue+"]"+"]>"+newLine;case nodeTypes.DOCUMENT_NODE:xhtml="";for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation);} 260return xhtml;default:return"";}}else{xhtml="";for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation+indentationUnit);} 261return xhtml;}} 262var layouts={};function createCommandLineFunctions(appender){ConsoleAppender.addGlobalCommandLineFunction("$",function(appender,args,returnValue){return document.getElementById(args[0]);});ConsoleAppender.addGlobalCommandLineFunction("dir",function(appender,args,returnValue){var lines=[];for(var i=0,len=args.length;i<len;i++){lines[i]=dir(args[i]);} 263return lines.join(newLine+newLine);});ConsoleAppender.addGlobalCommandLineFunction("dirxml",function(appender,args,returnValue){var lines=[];for(var i=0,len=args.length;i<len;i++){var win=appender.getCommandWindow();lines[i]=getXhtml(args[i]);} 264return lines.join(newLine+newLine);});ConsoleAppender.addGlobalCommandLineFunction("cd",function(appender,args,returnValue){var win,message;if(args.length===0||args[0]===""){win=window;message="Command line set to run in main window";}else{if(args[0].window==args[0]){win=args[0];message="Command line set to run in frame '"+args[0].name+"'";}else{win=window.frames[args[0]];if(win){message="Command line set to run in frame '"+args[0]+"'";}else{returnValue.isError=true;message="Frame '"+args[0]+"' does not exist";win=appender.getCommandWindow();}}} 265appender.setCommandWindow(win);return message;});ConsoleAppender.addGlobalCommandLineFunction("clear",function(appender,args,returnValue){returnValue.appendResult=false;appender.clear();});ConsoleAppender.addGlobalCommandLineFunction("keys",function(appender,args,returnValue){var keys=[];for(var k in args[0]){keys.push(k);} 266return keys;});ConsoleAppender.addGlobalCommandLineFunction("values",function(appender,args,returnValue){var values=[];for(var k in args[0]){try{values.push(args[0][k]);}catch(ex){logLog.warn("values(): Unable to obtain value for key "+k+". Details: "+getExceptionMessage(ex));}} 267return values;});ConsoleAppender.addGlobalCommandLineFunction("expansionDepth",function(appender,args,returnValue){var expansionDepth=parseInt(args[0],10);if(isNaN(expansionDepth)||expansionDepth<0){returnValue.isError=true;return""+args[0]+" is not a valid expansion depth";}else{appender.setCommandLineObjectExpansionDepth(expansionDepth);return"Object expansion depth set to "+expansionDepth;}});} 268function init(){createCommandLineFunctions();initialized=true;} 269init();})();function addWindowLoadListener(listener){var oldOnload=window.onload;if(typeof window.onload!="function"){window.onload=listener;}else{window.onload=function(evt){if(oldOnload){oldOnload(evt);} 270listener(evt);};}} 271addWindowLoadListener(function(){pageLoaded=true;log4javascript.dispatchEvent("load",{});});window.log4javascript=log4javascript;})();/* 272Copyright (c) 2011, Yahoo! Inc. All rights reserved. 273Code licensed under the BSD License:
274http://developer.yahoo.com/yui/license.html 275version: 2.9.0 276*/ 277/** 278 * The YAHOO object is the single global object used by YUI Library. It 279 * contains utility function for setting up namespaces, inheritance, and 280 * logging. YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces 281 * created automatically for and used by the library. 282 * @module yahoo 283 * @title YAHOO Global 284 */ 285 286/** 287 * YAHOO_config is not included as part of the library. Instead it is an 288 * object that can be defined by the implementer immediately before 289 * including the YUI library. The properties included in this object 290 * will be used to configure global properties needed as soon as the 291 * library begins to load. 292 * @class YAHOO_config 293 * @static 294 */ 295 296/** 297 * A reference to a function that will be executed every time a YAHOO module 298 * is loaded. As parameter, this function will receive the version 299 * information for the module. See <a href="YAHOO.env.html#getVersion"> 300 * YAHOO.env.getVersion</a> for the description of the version data structure. 301 * @property listener 302 * @type Function 303 * @static 304 * @default undefined 305 */ 306 307/** 308 * Set to true if the library will be dynamically loaded after window.onload. 309 * Defaults to false 310 * @property injecting 311 * @type boolean 312 * @static 313 * @default undefined 314 */ 315 316/** 317 * Instructs the yuiloader component to dynamically load yui components and 318 * their dependencies. See the yuiloader documentation for more information 319 * about dynamic loading 320 * @property load 321 * @static 322 * @default undefined 323 * @see yuiloader 324 */ 325 326/** 327 * Forces the use of the supplied locale where applicable in the library 328 * @property locale 329 * @type string 330 * @static 331 * @default undefined 332 */ 333 334if (typeof YAHOO == "undefined" || !YAHOO) { 335 /** 336 * The YAHOO global namespace object. If YAHOO is already defined, the 337 * existing YAHOO object will not be overwritten so that defined 338 * namespaces are preserved. 339 * @class YAHOO 340 * @static 341 */ 342 var YAHOO = {}; 343} 344 345/** 346 * Returns the namespace specified and creates it if it doesn't exist 347 * <pre> 348 * YAHOO.namespace("property.package"); 349 * YAHOO.namespace("YAHOO.property.package"); 350 * </pre> 351 * Either of the above would create YAHOO.property, then 352 * YAHOO.property.package 353 * 354 * Be careful when naming packages. Reserved words may work in some browsers 355 * and not others. For instance, the following will fail in Safari: 356 * <pre> 357 * YAHOO.namespace("really.long.nested.namespace"); 358 * </pre> 359 * This fails because "long" is a future reserved word in ECMAScript 360 * 361 * For implementation code that uses YUI, do not create your components 362 * in the namespaces defined by YUI ( 363 * <code>YAHOO.util</code>, 364 * <code>YAHOO.widget</code>, 365 * <code>YAHOO.lang</code>, 366 * <code>YAHOO.tool</code>, 367 * <code>YAHOO.example</code>, 368 * <code>YAHOO.env</code>) -- create your own namespace (e.g., 'companyname'). 369 * 370 * @method namespace 371 * @static 372 * @param {String*} arguments 1-n namespaces to create 373 * @return {Object} A reference to the last namespace object created 374 */ 375YAHOO.namespace = function() { 376 var a=arguments, o=null, i, j, d; 377 for (i=0; i<a.length; i=i+1) { 378 d=(""+a[i]).split("."); 379 o=YAHOO; 380 381 // YAHOO is implied, so it is ignored if it is included 382 for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) { 383 o[d[j]]=o[d[j]] || {}; 384 o=o[d[j]]; 385 } 386 } 387 388 return o; 389}; 390 391/** 392 * Uses YAHOO.widget.Logger to output a log message, if the widget is 393 * available. 394 * Note: LogReader adds the message, category, and source to the DOM as HTML. 395 * 396 * @method log 397 * @static 398 * @param {HTML} msg The message to log. 399 * @param {HTML} cat The log category for the message. Default 400 * categories are "info", "warn", "error", time". 401 * Custom categories can be used as well. (opt) 402 * @param {HTML} src The source of the the message (opt) 403 * @return {Boolean} True if the log operation was successful. 404 */ 405YAHOO.log = function(msg, cat, src) { 406 var l=YAHOO.widget.Logger; 407 if(l && l.log) { 408 return l.log(msg, cat, src); 409 } else { 410 return false;
411 } 412}; 413 414/** 415 * Registers a module with the YAHOO object 416 * @method register 417 * @static 418 * @param {String} name the name of the module (event, slider, etc) 419 * @param {Function} mainClass a reference to class in the module. This 420 * class will be tagged with the version info 421 * so that it will be possible to identify the 422 * version that is in use when multiple versions 423 * have loaded 424 * @param {Object} data metadata object for the module. Currently it 425 * is expected to contain a "version" property 426 * and a "build" property at minimum. 427 */ 428YAHOO.register = function(name, mainClass, data) { 429 var mods = YAHOO.env.modules, m, v, b, ls, i; 430 431 if (!mods[name]) { 432 mods[name] = { 433 versions:[], 434 builds:[] 435 }; 436 } 437 438 m = mods[name]; 439 v = data.version; 440 b = data.build; 441 ls = YAHOO.env.listeners; 442 443 m.name = name; 444 m.version = v; 445 m.build = b; 446 m.versions.push(v); 447 m.builds.push(b); 448 m.mainClass = mainClass; 449 450 // fire the module load listeners 451 for (i=0;i<ls.length;i=i+1) { 452 ls[i](m); 453 } 454 // label the main class 455 if (mainClass) { 456 mainClass.VERSION = v; 457 mainClass.BUILD = b; 458 } else { 459 YAHOO.log("mainClass is undefined for module " + name, "warn"); 460 } 461}; 462 463/** 464 * YAHOO.env is used to keep track of what is known about the YUI library and 465 * the browsing environment 466 * @class YAHOO.env 467 * @static 468 */ 469YAHOO.env = YAHOO.env || { 470 471 /** 472 * Keeps the version info for all YUI modules that have reported themselves 473 * @property modules 474 * @type Object[] 475 */ 476 modules: [], 477 478 /** 479 * List of functions that should be executed every time a YUI module 480 * reports itself. 481 * @property listeners 482 * @type Function[] 483 */ 484 listeners: [] 485}; 486 487/** 488 * Returns the version data for the specified module: 489 * <dl> 490 * <dt>name:</dt> <dd>The name of the module</dd> 491 * <dt>version:</dt> <dd>The version in use</dd> 492 * <dt>build:</dt> <dd>The build number in use</dd> 493 * <dt>versions:</dt> <dd>All versions that were registered</dd> 494 * <dt>builds:</dt> <dd>All builds that were registered.</dd> 495 * <dt>mainClass:</dt> <dd>An object that was was stamped with the 496 * current version and build. If 497 * mainClass.VERSION != version or mainClass.BUILD != build, 498 * multiple versions of pieces of the library have been 499 * loaded, potentially causing issues.</dd> 500 * </dl> 501 * 502 * @method getVersion 503 * @static 504 * @param {String} name the name of the module (event, slider, etc) 505 * @return {Object} The version info 506 */ 507YAHOO.env.getVersion = function(name) { 508 return YAHOO.env.modules[name] || null; 509}; 510 511/** 512 * Do not fork for a browser if it can be avoided. Use feature detection when 513 * you can. Use the user agent as a last resort. YAHOO.env.ua stores a version 514 * number for the browser engine, 0 otherwise. This value may or may not map 515 * to the version number of the browser using the engine. The value is 516 * presented as a float so that it can easily be used for boolean evaluation 517 * as well as for looking for a particular range of versions. Because of this, 518 * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9 519 * reports 1.8). 520 * @class YAHOO.env.ua 521 * @static 522 */ 523 524/** 525 * parses a user agent string (or looks for one in navigator to parse if 526 * not supplied). 527 * @method parseUA 528 * @since 2.9.0 529 * @static 530 */ 531YAHOO.env.parseUA = function(agent) { 532 533 var numberify = function(s) { 534 var c = 0; 535 return parseFloat(s.replace(/\./g, function() { 536 return (c++ == 1) ? '' : '.'; 537 })); 538 }, 539 540 nav = navigator, 541 542 o = { 543 544 /** 545 * Internet Explorer version number or 0. Example: 6 546 * @property ie 547 * @type float 548 * @static 549 */ 550 ie: 0, 551 552 /** 553 * Opera version number or 0. Example: 9.2 554 * @property opera 555 * @type float 556 * @static 557 */ 558 opera: 0, 559 560 /** 561 * Gecko engine revision number. Will evaluate to 1 if Gecko 562 * is detected but the revision could not be found. Other browsers 563 * will be 0. Example: 1.8 564 * <pre> 565 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 566 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8 567 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81 568 * Firefox 3.0 <-- 1.9 569 * Firefox 3.5 <-- 1.91 570 * </pre> 571 * @property gecko 572 * @type float 573 * @static 574 */ 575 gecko: 0, 576 577 /** 578 * AppleWebKit version. KHTML browsers that are not WebKit browsers 579 * will evaluate to 1, other browsers 0. Example: 418.9 580 * <pre> 581 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 582 * latest available for Mac OSX 10.3. 583 * Safari 2.0.2: 416 <-- hasOwnProperty introduced 584 * Safari 2.0.4: 418 <-- preventDefault fixed 585 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run 586 * different versions of webkit 587 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been 588 * updated, but not updated 589 * to the latest patch. 590 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native 591 * SVG and many major issues fixed). 592 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic 593 * update from 2.x via the 10.4.11 OS patch. 594 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event. 595 * yahoo.com user agent hack removed. 596 * </pre> 597 * http://en.wikipedia.org/wiki/Safari_version_history 598 * @property webkit 599 * @type float 600 * @static 601 */ 602 webkit: 0, 603 604 /** 605 * Chrome will be detected as webkit, but this property will also 606 * be populated with the Chrome version number 607 * @property chrome 608 * @type float 609 * @static 610 */ 611 chrome: 0, 612 613 /** 614 * The mobile property will be set to a string containing any relevant 615 * user agent information when a modern mobile browser is detected. 616 * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series 617 * devices with the WebKit-based browser, and Opera Mini. 618 * @property mobile 619 * @type string 620 * @static 621 */ 622 mobile: null, 623 624 /** 625 * Adobe AIR version number or 0. Only populated if webkit is detected. 626 * Example: 1.0 627 * @property air 628 * @type float 629 */ 630 air: 0, 631 /** 632 * Detects Apple iPad's OS version 633 * @property ipad 634 * @type float 635 * @static 636 */ 637 ipad: 0, 638 /** 639 * Detects Apple iPhone's OS version 640 * @property iphone 641 * @type float 642 * @static 643 */ 644 iphone: 0, 645 /** 646 * Detects Apples iPod's OS version 647 * @property ipod 648 * @type float 649 * @static 650 */ 651 ipod: 0, 652 /** 653 * General truthy check for iPad, iPhone or iPod 654 * @property ios 655 * @type float 656 * @static 657 */ 658 ios: null, 659 /** 660 * Detects Googles Android OS version 661 * @property android 662 * @type float 663 * @static 664 */ 665 android: 0, 666 /** 667 * Detects Palms WebOS version 668 * @property webos 669 * @type float 670 * @static 671 */ 672 webos: 0, 673 674 /** 675 * Google Caja version number or 0. 676 * @property caja 677 * @type float 678 */ 679 caja: nav && nav.cajaVersion, 680 681 /** 682 * Set to true if the page appears to be in SSL 683 * @property secure 684 * @type boolean 685 * @static 686 */ 687 secure: false, 688 689 /** 690 * The operating system. Currently only detecting windows or macintosh 691 * @property os 692 * @type string 693 * @static 694 */ 695 os: null 696 697 }, 698 699 ua = agent || (navigator && navigator.userAgent), 700 701 loc = window && window.location, 702 703 href = loc && loc.href, 704 705 m; 706 707 o.secure = href && (href.toLowerCase().indexOf("https") === 0); 708 709 if (ua) { 710 711 if ((/windows|win32/i).test(ua)) { 712 o.os = 'windows'; 713 } else if ((/macintosh/i).test(ua)) { 714 o.os = 'macintosh'; 715 } else if ((/rhino/i).test(ua)) { 716 o.os = 'rhino'; 717 } 718 719 // Modern KHTML browsers should qualify as Safari X-Grade 720 if ((/KHTML/).test(ua)) { 721 o.webkit = 1; 722 } 723 // Modern WebKit browsers are at least X-Grade
724 m = ua.match(/AppleWebKit\/([^\s]*)/); 725 if (m && m[1]) { 726 o.webkit = numberify(m[1]); 727 728 // Mobile browser check 729 if (/ Mobile\//.test(ua)) { 730 o.mobile = 'Apple'; // iPhone or iPod Touch 731 732 m = ua.match(/OS ([^\s]*)/); 733 if (m && m[1]) { 734 m = numberify(m[1].replace('_', '.')); 735 } 736 o.ios = m; 737 o.ipad = o.ipod = o.iphone = 0; 738 739 m = ua.match(/iPad|iPod|iPhone/); 740 if (m && m[0]) { 741 o[m[0].toLowerCase()] = o.ios; 742 } 743 } else { 744 m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/); 745 if (m) { 746 // Nokia N-series, Android, webOS, ex: NokiaN95 747 o.mobile = m[0]; 748 } 749 if (/webOS/.test(ua)) { 750 o.mobile = 'WebOS'; 751 m = ua.match(/webOS\/([^\s]*);/); 752 if (m && m[1]) { 753 o.webos = numberify(m[1]); 754 } 755 } 756 if (/ Android/.test(ua)) { 757 o.mobile = 'Android'; 758 m = ua.match(/Android ([^\s]*);/); 759 if (m && m[1]) { 760 o.android = numberify(m[1]); 761 } 762 763 } 764 } 765 766 m = ua.match(/Chrome\/([^\s]*)/); 767 if (m && m[1]) { 768 o.chrome = numberify(m[1]); // Chrome 769 } else { 770 m = ua.match(/AdobeAIR\/([^\s]*)/); 771 if (m) { 772 o.air = m[0]; // Adobe AIR 1.0 or better 773 } 774 } 775 } 776 777 if (!o.webkit) { // not webkit 778// @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr) 779 m = ua.match(/Opera[\s\/]([^\s]*)/); 780 if (m && m[1]) { 781 o.opera = numberify(m[1]); 782 m = ua.match(/Version\/([^\s]*)/); 783 if (m && m[1]) { 784 o.opera = numberify(m[1]); // opera 10+ 785 } 786 m = ua.match(/Opera Mini[^;]*/); 787 if (m) { 788 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316 789 } 790 } else { // not opera or webkit 791 m = ua.match(/MSIE ([^;]*)|Trident.*; rv.([0-9.]+)/); 792 if (m && (m[1] || m[2])) { 793 o.ie = numberify(m[1] || m[2]); 794 } else { // not opera, webkit, or ie 795 m = ua.match(/Gecko\/([^\s]*)/); 796 if (m) { 797 o.gecko = 1; // Gecko detected, look for revision 798 m = ua.match(/rv:([^\s\)]*)/); 799 if (m && m[1]) { 800 o.gecko = numberify(m[1]); 801 } 802 } 803 } 804 } 805 } 806 } 807 808 return o; 809}; 810 811YAHOO.env.ua = YAHOO.env.parseUA(); 812 813/* 814 * Initializes the global by creating the default namespaces and applying 815 * any new configuration information that is detected. This is the setup 816 * for env. 817 * @method init 818 * @static 819 * @private 820 */ 821(function() { 822 YAHOO.namespace("util", "widget", "example"); 823 /*global YAHOO_config*/ 824 if ("undefined" !== typeof YAHOO_config) { 825 var l=YAHOO_config.listener, ls=YAHOO.env.listeners,unique=true, i; 826 if (l) { 827 // if YAHOO is loaded multiple times we need to check to see if 828 // this is a new config object. If it is, add the new component 829 // load listener to the stack 830 for (i=0; i<ls.length; i++) { 831 if (ls[i] == l) { 832 unique = false; 833 break; 834 } 835 } 836 837 if (unique) { 838 ls.push(l); 839 } 840 } 841 } 842})(); 843/** 844 * Provides the language utilites and extensions used by the library 845 * @class YAHOO.lang 846 */ 847YAHOO.lang = YAHOO.lang || {}; 848 849(function() { 850 851 852var L = YAHOO.lang, 853 854 OP = Object.prototype, 855 ARRAY_TOSTRING = '[object Array]', 856 FUNCTION_TOSTRING = '[object Function]', 857 OBJECT_TOSTRING = '[object Object]', 858 NOTHING = [], 859 860 HTML_CHARS = { 861 '&': '&', 862 '<': '<', 863 '>': '>', 864 '"': '"', 865 "'": ''', 866 '/': '/', 867 '`': '`' 868 }, 869 870 // ADD = ["toString", "valueOf", "hasOwnProperty"], 871 ADD = ["toString", "valueOf"], 872 873 OB = { 874 875 /** 876 * Determines wheather or not the provided object is an array.
877 * @method isArray 878 * @param {any} o The object being testing 879 * @return {boolean} the result 880 */ 881 isArray: function(o) { 882 return OP.toString.apply(o) === ARRAY_TOSTRING; 883 }, 884 885 /** 886 * Determines whether or not the provided object is a boolean 887 * @method isBoolean 888 * @param {any} o The object being testing 889 * @return {boolean} the result 890 */ 891 isBoolean: function(o) { 892 return typeof o === 'boolean'; 893 }, 894 895 /** 896 * Determines whether or not the provided object is a function. 897 * Note: Internet Explorer thinks certain functions are objects: 898 * 899 * var obj = document.createElement("object"); 900 * YAHOO.lang.isFunction(obj.getAttribute) // reports false in IE 901 * 902 * var input = document.createElement("input"); // append to body 903 * YAHOO.lang.isFunction(input.focus) // reports false in IE 904 * 905 * You will have to implement additional tests if these functions 906 * matter to you. 907 * 908 * @method isFunction 909 * @param {any} o The object being testing 910 * @return {boolean} the result 911 */ 912 isFunction: function(o) { 913 return (typeof o === 'function') || OP.toString.apply(o) === FUNCTION_TOSTRING; 914 }, 915 916 /** 917 * Determines whether or not the provided object is null 918 * @method isNull 919 * @param {any} o The object being testing 920 * @return {boolean} the result 921 */ 922 isNull: function(o) { 923 return o === null; 924 }, 925 926 /** 927 * Determines whether or not the provided object is a legal number 928 * @method isNumber 929 * @param {any} o The object being testing 930 * @return {boolean} the result 931 */ 932 isNumber: function(o) { 933 return typeof o === 'number' && isFinite(o); 934 }, 935 936 /** 937 * Determines whether or not the provided object is of type object 938 * or function 939 * @method isObject 940 * @param {any} o The object being testing 941 * @return {boolean} the result 942 */ 943 isObject: function(o) { 944return (o && (typeof o === 'object' || L.isFunction(o))) || false; 945 }, 946 947 /** 948 * Determines whether or not the provided object is a string 949 * @method isString 950 * @param {any} o The object being testing 951 * @return {boolean} the result 952 */ 953 isString: function(o) { 954 return typeof o === 'string'; 955 }, 956 957 /** 958 * Determines whether or not the provided object is undefined 959 * @method isUndefined 960 * @param {any} o The object being testing 961 * @return {boolean} the result 962 */ 963 isUndefined: function(o) { 964 return typeof o === 'undefined'; 965 }, 966 967 968 /** 969 * IE will not enumerate native functions in a derived object even if the 970 * function was overridden. This is a workaround for specific functions 971 * we care about on the Object prototype. 972 * @property _IEEnumFix 973 * @param {Function} r the object to receive the augmentation 974 * @param {Function} s the object that supplies the properties to augment 975 * @static 976 * @private 977 */ 978 _IEEnumFix: (YAHOO.env.ua.ie) ? function(r, s) { 979 var i, fname, f; 980 for (i=0;i<ADD.length;i=i+1) { 981 982 fname = ADD[i]; 983 f = s[fname]; 984 985 if (L.isFunction(f) && f!=OP[fname]) { 986 r[fname]=f; 987 } 988 } 989 } : function(){}, 990 991 /** 992 * <p> 993 * Returns a copy of the specified string with special HTML characters 994 * escaped. The following characters will be converted to their 995 * corresponding character entities: 996 * <code>& < > " ' / `</code> 997 * </p> 998 * 999 * <p> 1000 * This implementation is based on the 1001 * <a href="http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet">OWASP 1002 * HTML escaping recommendations</a>. In addition to the characters 1003 * in the OWASP recommendation, we also escape the <code>`</code> 1004 * character, since IE interprets it as an attribute delimiter when used in 1005 * innerHTML. 1006 * </p> 1007 * 1008 * @method escapeHTML 1009 * @param {String} html String to escape. 1010 * @return {String} Escaped string. 1011 * @static 1012 * @since 2.9.0 1013 */ 1014 escapeHTML: function (html) { 1015 return html.replace(/[&<>"'\/`]/g, function (match) { 1016 return HTML_CHARS[match]; 1017 }); 1018 }, 1019 1020 /** 1021 * Utility to set up the prototype, constructor and superclass properties to 1022 * support an inheritance strategy that can chain constructors and methods. 1023 * Static members will not be inherited. 1024 * 1025 * @method extend 1026 * @static 1027 * @param {Function} subc the object to modify 1028 * @param {Function} superc the object to inherit 1029 * @param {Object} overrides additional properties/methods to add to the 1030 * subclass prototype. These will override the 1031 * matching items obtained from the superclass 1032 * if present. 1033 */ 1034 extend: function(subc, superc, overrides) { 1035 if (!superc||!subc) { 1036 throw new Error("extend failed, please check that " + 1037 "all dependencies are included."); 1038 } 1039 var F = function() {}, i; 1040 F.prototype=superc.prototype; 1041 subc.prototype=new F(); 1042 subc.prototype.constructor=subc; 1043 subc.superclass=superc.prototype; 1044 if (superc.prototype.constructor == OP.constructor) { 1045 superc.prototype.constructor=superc; 1046 } 1047 1048 if (overrides) { 1049 for (i in overrides) { 1050 if (L.hasOwnProperty(overrides, i)) { 1051 subc.prototype[i]=overrides[i]; 1052 } 1053 } 1054 1055 L._IEEnumFix(subc.prototype, overrides); 1056 } 1057 }, 1058 1059 /** 1060 * Applies all properties in the supplier to the receiver if the 1061 * receiver does not have these properties yet. Optionally, one or 1062 * more methods/properties can be specified (as additional
1063 * parameters). This option will overwrite the property if receiver 1064 * has it already. If true is passed as the third parameter, all 1065 * properties will be applied and _will_ overwrite properties in 1066 * the receiver. 1067 * 1068 * @method augmentObject 1069 * @static 1070 * @since 2.3.0 1071 * @param {Function} r the object to receive the augmentation 1072 * @param {Function} s the object that supplies the properties to augment 1073 * @param {String*|boolean} arguments zero or more properties methods 1074 * to augment the receiver with. If none specified, everything 1075 * in the supplier will be used unless it would 1076 * overwrite an existing property in the receiver. If true 1077 * is specified as the third parameter, all properties will 1078 * be applied and will overwrite an existing property in 1079 * the receiver 1080 */ 1081 augmentObject: function(r, s) { 1082 if (!s||!r) { 1083 throw new Error("Absorb failed, verify dependencies."); 1084 } 1085 var a=arguments, i, p, overrideList=a[2]; 1086 if (overrideList && overrideList!==true) { // only absorb the specified properties 1087 for (i=2; i<a.length; i=i+1) { 1088 r[a[i]] = s[a[i]]; 1089 } 1090 } else { // take everything, overwriting only if the third parameter is true 1091 for (p in s) { 1092 if (overrideList || !(p in r)) { 1093 r[p] = s[p]; 1094 } 1095 } 1096 1097 L._IEEnumFix(r, s); 1098 } 1099 1100 return r; 1101 }, 1102 1103 /** 1104 * Same as YAHOO.lang.augmentObject, except it only applies prototype properties 1105 * @see YAHOO.lang.augmentObject 1106 * @method augmentProto 1107 * @static 1108 * @param {Function} r the object to receive the augmentation 1109 * @param {Function} s the object that supplies the properties to augment 1110 * @param {String*|boolean} arguments zero or more properties methods 1111 * to augment the receiver with. If none specified, everything 1112 * in the supplier will be used unless it would overwrite an existing 1113 * property in the receiver. if true is specified as the third 1114 * parameter, all properties will be applied and will overwrite an 1115 * existing property in the receiver 1116 */ 1117 augmentProto: function(r, s) { 1118 if (!s||!r) { 1119 throw new Error("Augment failed, verify dependencies."); 1120 } 1121 //var a=[].concat(arguments); 1122 var a=[r.prototype,s.prototype], i; 1123 for (i=2;i<arguments.length;i=i+1) { 1124 a.push(arguments[i]); 1125 } 1126 L.augmentObject.apply(this, a); 1127 1128 return r; 1129 }, 1130 1131 1132 /** 1133 * Returns a simple string representation of the object or array. 1134 * Other types of objects will be returned unprocessed. Arrays 1135 * are expected to be indexed. Use object notation for 1136 * associative arrays. 1137 * @method dump 1138 * @since 2.3.0 1139 * @param o {Object} The object to dump 1140 * @param d {int} How deep to recurse child objects, default 3 1141 * @return {String} the dump result 1142 */ 1143 dump: function(o, d) { 1144 var i,len,s=[],OBJ="{...}",FUN="f(){...}", 1145 COMMA=', ', ARROW=' => '; 1146 1147 // Cast non-objects to string 1148 // Skip dates because the std toString is what we want 1149 // Skip HTMLElement-like objects because trying to dump 1150 // an element will cause an unhandled exception in FF 2.x 1151 if (!L.isObject(o)) { 1152 return o + ""; 1153 } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) { 1154 return o; 1155 } else if (L.isFunction(o)) { 1156 return FUN; 1157 } 1158 1159 // dig into child objects the depth specifed. Default 3 1160 d = (L.isNumber(d)) ? d : 3; 1161 1162 // arrays [1, 2, 3] 1163 if (L.isArray(o)) { 1164 s.push("["); 1165 for (i=0,len=o.length;i<len;i=i+1) { 1166 if (L.isObject(o[i])) { 1167 s.push((d > 0) ? L.dump(o[i], d-1) : OBJ); 1168 } else { 1169 s.push(o[i]); 1170 } 1171 s.push(COMMA); 1172 } 1173 if (s.length > 1) { 1174 s.pop(); 1175 } 1176 s.push("]"); 1177 // objects {k1 => v1, k2 => v2} 1178 } else { 1179 s.push("{"); 1180 for (i in o) { 1181 if (L.hasOwnProperty(o, i)) { 1182 s.push(i + ARROW); 1183 if (L.isObject(o[i])) { 1184 s.push((d > 0) ? L.dump(o[i], d-1) : OBJ); 1185 } else { 1186 s.push(o[i]); 1187 } 1188 s.push(COMMA); 1189 } 1190 } 1191 if (s.length > 1) { 1192 s.pop(); 1193 } 1194 s.push("}"); 1195 } 1196 1197 return s.join(""); 1198 }, 1199 1200 /** 1201 * Does variable substitution on a string. It scans through the string 1202 * looking for expressions enclosed in { } braces. If an expression 1203 * is found, it is used a key on the object. If there is a space in 1204 * the key, the first word is used for the key and the rest is provided 1205 * to an optional function to be used to programatically determine the 1206 * value (the extra information might be used for this decision). If 1207 * the value for the key in the object, or what is returned from the 1208 * function has a string value, number value, or object value, it is 1209 * substituted for the bracket expression and it repeats. If this 1210 * value is an object, it uses the Object's toString() if this has 1211 * been overridden, otherwise it does a shallow dump of the key/value 1212 * pairs. 1213 * 1214 * By specifying the recurse option, the string is rescanned after 1215 * every replacement, allowing for nested template substitutions. 1216 * The side effect of this option is that curly braces in the 1217 * replacement content must be encoded. 1218 * 1219 * @method substitute 1220 * @since 2.3.0 1221 * @param s {String} The string that will be modified. 1222 * @param o {Object}
1222 An object containing the replacement values 1223 * @param f {Function} An optional function that can be used to 1224 * process each match. It receives the key, 1225 * value, and any extra metadata included with 1226 * the key inside of the braces. 1227 * @param recurse {boolean} default true - if not false, the replaced 1228 * string will be rescanned so that nested substitutions are possible. 1229 * @return {String} the substituted string 1230 */ 1231 substitute: function (s, o, f, recurse) { 1232 var i, j, k, key, v, meta, saved=[], token, lidx=s.length, 1233 DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}', 1234 dump, objstr; 1235 1236 for (;;) { 1237 i = s.lastIndexOf(LBRACE, lidx); 1238 if (i < 0) { 1239 break; 1240 } 1241 j = s.indexOf(RBRACE, i); 1242 if (i + 1 > j) { 1243 break; 1244 } 1245 1246 //Extract key and meta info 1247 token = s.substring(i + 1, j); 1248 key = token; 1249 meta = null; 1250 k = key.indexOf(SPACE); 1251 if (k > -1) { 1252 meta = key.substring(k + 1); 1253 key = key.substring(0, k); 1254 } 1255 1256 // lookup the value 1257 v = o[key]; 1258 1259 // if a substitution function was provided, execute it 1260 if (f) { 1261 v = f(key, v, meta); 1262 } 1263 1264 if (L.isObject(v)) { 1265 if (L.isArray(v)) { 1266 v = L.dump(v, parseInt(meta, 10)); 1267 } else { 1268 meta = meta || ""; 1269 1270 // look for the keyword 'dump', if found force obj dump 1271 dump = meta.indexOf(DUMP); 1272 if (dump > -1) { 1273 meta = meta.substring(4); 1274 } 1275 1276 objstr = v.toString(); 1277 1278 // use the toString if it is not the Object toString 1279 // and the 'dump' meta info was not found 1280 if (objstr === OBJECT_TOSTRING || dump > -1) { 1281 v = L.dump(v, parseInt(meta, 10)); 1282 } else { 1283 v = objstr; 1284 } 1285 } 1286 } else if (!L.isString(v) && !L.isNumber(v)) { 1287 // This {block} has no replace string. Save it for later. 1288 v = "~-" + saved.length + "-~"; 1289 saved[saved.length] = token; 1290 1291 // break; 1292 } 1293 1294 s = s.substring(0, i) + v + s.substring(j + 1); 1295 1296 if (recurse === false) { 1297 lidx = i-1; 1298 } 1299 1300 } 1301 1302 // restore saved {block}s 1303 for (i=saved.length-1; i>=0; i=i-1) { 1304 s = s.replace(new RegExp("~-" + i + "-~"), "{" + saved[i] + "}", "g"); 1305 } 1306 1307 return s; 1308 }, 1309 1310 1311 /** 1312 * Returns a string without any leading or trailing whitespace. If 1313 * the input is not a string, the input will be returned untouched. 1314 * @method trim 1315 * @since 2.3.0 1316 * @param s {string} the string to trim 1317 * @return {string} the trimmed string 1318 */ 1319 trim: function(s){ 1320 try { 1321 return s.replace(/^\s+|\s+$/g, ""); 1322 } catch(e) { 1323 return s; 1324 } 1325 }, 1326 1327 /** 1328 * Returns a new object containing all of the properties of 1329 * all the supplied objects. The properties from later objects 1330 * will overwrite those in earlier objects. 1331 * @method merge 1332 * @since 2.3.0 1333 * @param arguments {Object*} the objects to merge 1334 * @return the new merged object 1335 */ 1336 merge: function() { 1337 var o={}, a=arguments, l=a.length, i; 1338 for (i=0; i<l; i=i+1) { 1339 L.augmentObject(o, a[i], true); 1340 } 1341 return o; 1342 }, 1343 1344 /** 1345 * Executes the supplied function in the context of the supplied 1346 * object 'when' milliseconds later. Executes the function a 1347 * single time unless periodic is set to true. 1348 * @method later 1349 * @since 2.4.0 1350 * @param when {int} the number of milliseconds to wait until the fn 1351 * is executed 1352 * @param o the context object 1353 * @param fn {Function|String} the function to execute or the name of 1354 * the method in the 'o' object to execute 1355 * @param data [Array] data that is provided to the function. This accepts 1356 * either a single item or an array. If an array is provided, the 1357 * function is executed with one parameter for each array item. If 1358 * you need to pass a single array parameter, it needs to be wrapped in 1359 * an array [myarray] 1360 * @param periodic {boolean} if true, executes continuously at supplied 1361 * interval until canceled 1362 * @return a timer object. Call the cancel() method on this object to 1363 * stop the timer. 1364 */ 1365 later: function(when, o, fn, data, periodic) { 1366 when = when || 0; 1367 o = o || {}; 1368 var m=fn, d=data, f, r; 1369 1370 if (L.isString(fn)) { 1371 m = o[fn]; 1372 } 1373 1374 if (!m) { 1375 throw new TypeError("method undefined"); 1376 } 1377 1378 if (!L.isUndefined(data) && !L.isArray(d)) { 1379 d = [data]; 1380 } 1381 1382 f = function() { 1383 m.apply(o, d || NOTHING); 1384 }; 1385 1386 r = (periodic) ? setInterval(f, when) : setTimeout(f, when); 1387 1388 return { 1389 interval: periodic, 1390 cancel: function() { 1391 if (this.interval) { 1392 clearInterval(r); 1393 } else { 1394 clearTimeout(r); 1395 } 1396 } 1397 }; 1398 }, 1399 1400 /** 1401 * A convenience method for detecting a legitimate non-null value. 1402 * Returns false for null/undefined/NaN, true for other values, 1403 * including 0/false/'' 1404 * @method isValue 1405 * @since 2.3.0 1406 * @param o {any} the item to test 1407 * @return {boolean} true if it is not null/undefined/NaN || false 1408 */ 1409 isValue: function(o) { 1410 // return (o || o === false || o === 0 || o === ''); // Infinity fails 1411return (L.isObject(o) || L.isString(o) || L.isNumber(o) || L.isBoolean(o)); 1412 } 1413 1414}; 1415 1416/** 1417 * Determines whether or not the property was added 1418 * to the object instance. Returns false if the property is not present 1419 * in the object, or was inherited from the prototype. 1420 * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x. 1421 * There is a discrepancy between YAHOO.lang.hasOwnProperty and 1422 * Object.prototype.hasOwnProperty when the property is a primitive added to 1423 * both the instance AND prototype with the same value: 1424 * <pre> 1425 * var A = function() {}; 1426 * A.prototype.foo = 'foo'; 1427 * var a = new A(); 1428 * a.foo = 'foo'; 1429 * alert(a.hasOwnProperty('foo')); // true 1430 * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback 1431 * </pre> 1432 * @method hasOwnProperty 1433 * @param {any} o The object being testing 1434 * @param prop {string} the name of the property to test 1435 * @return {boolean} the result 1436 */ 1437L.hasOwnProperty = (OP.hasOwnProperty) ? 1438 function(o, prop) { 1439 return o && o.hasOwnProperty && o.hasOwnProperty(prop); 1440 } : function(o, prop) { 1441 return !L.isUndefined(o[prop]) && 1442 o.constructor.prototype[prop] !== o[prop]; 1443 }; 1444 1445// new lang wins 1446OB.augmentObject(L, OB, true); 1447 1448/* 1449 * An alias for <a href="YAHOO.lang.html">YAHOO.lang</a> 1450 * @class YAHOO.util.Lang 1451 */ 1452YAHOO.util.Lang = L; 1453 1454/** 1455 * Same as YAHOO.lang.augmentObject, except it only applies prototype 1456 * properties. This is an alias for augmentProto. 1457 * @see YAHOO.lang.augmentObject 1458 * @method augment 1459 * @static 1460 * @param {Function} r the object to receive the augmentation 1461 * @param {Function} s the object that supplies the properties to augment 1462 * @param {String*|boolean} arguments zero or more properties methods to 1463 * augment the receiver with. If none specified, everything 1464 * in the supplier will be used unless it would 1465 * overwrite an existing property in the receiver. if true 1466 * is specified as the third parameter, all properties will
1467 * be applied and will overwrite an existing property in 1468 * the receiver 1469 */ 1470L.augment = L.augmentProto; 1471 1472/** 1473 * An alias for <a href="YAHOO.lang.html#augment">YAHOO.lang.augment</a> 1474 * @for YAHOO 1475 * @method augment 1476 * @static 1477 * @param {Function} r the object to receive the augmentation 1478 * @param {Function} s the object that supplies the properties to augment 1479 * @param {String*} arguments zero or more properties methods to 1480 * augment the receiver with. If none specified, everything 1481 * in the supplier will be used unless it would 1482 * overwrite an existing property in the receiver 1483 */ 1484YAHOO.augment = L.augmentProto; 1485 1486/** 1487 * An alias for <a href="YAHOO.lang.html#extend">YAHOO.lang.extend</a> 1488 * @method extend 1489 * @static 1490 * @param {Function} subc the object to modify 1491 * @param {Function} superc the object to inherit 1492 * @param {Object} overrides additional properties/methods to add to the 1493 * subclass prototype. These will override the 1494 * matching items obtained from the superclass if present. 1495 */ 1496YAHOO.extend = L.extend; 1497 1498})(); 1499YAHOO.register("yahoo", YAHOO, {version: "2.9.0", build: "2800"}); 1500/* 1501Copyright (c) 2011, Yahoo! Inc. All rights reserved. 1502Code licensed under the BSD License: 1503http://developer.yahoo.com/yui/license.html 1504version: 2.9.0 1505*/ 1506 1507/** 1508 * The CustomEvent class lets you define events for your application 1509 * that can be subscribed to by one or more independent component. 1510 * 1511 * @param {String} type The type of event, which is passed to the callback 1512 * when the event fires 1513 * @param {Object} context The context the event will fire from. "this" will 1514 * refer to this object in the callback. Default value: 1515 * the window object. The listener can override this. 1516 * @param {boolean} silent pass true to prevent the event from writing to 1517 * the debugsystem 1518 * @param {int} signature the signature that the custom event subscriber 1519 * will receive. YAHOO.util.CustomEvent.LIST or 1520 * YAHOO.util.CustomEvent.FLAT. The default is 1521 * YAHOO.util.CustomEvent.LIST. 1522 * @param fireOnce {boolean} If configured to fire once, the custom event 1523 * will only notify subscribers a single time regardless of how many times 1524 * the event is fired. In addition, new subscribers will be notified 1525 * immediately if the event has already been fired. 1526 * @namespace YAHOO.util 1527 * @class CustomEvent 1528 * @constructor 1529 */ 1530YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) { 1531 1532 /** 1533 * The type of event, returned to subscribers when the event fires 1534 * @property type 1535 * @type string 1536 */ 1537 this.type = type; 1538 1539 /** 1540 * The context the event will fire from by default. Defaults to the window obj. 1541 * @property scope 1542 * @type object 1543 */ 1544 this.scope = context || window; 1545 1546 /** 1547 * By default all custom events are logged in the debug build. Set silent to true 1548 * to disable debug output for this event. 1549 * @property silent 1550 * @type boolean 1551 */ 1552 this.silent = silent; 1553 1554 /** 1555 * If configured to fire once, the custom event will only notify subscribers 1556 * a single time regardless of how many times the event is fired. In addition, 1557 * new subscribers will be notified immediately if the event has already been 1558 * fired. 1559 * @property fireOnce 1560 * @type boolean 1561 * @default false 1562 */ 1563 this.fireOnce = fireOnce; 1564 1565 /** 1566 * Indicates whether or not this event has ever been fired. 1567 * @property fired 1568 * @type boolean 1569 * @default false 1570 */ 1571 this.fired = false; 1572 1573 /** 1574 * For fireOnce events the arguments the event was fired with are stored 1575 * so that new subscribers get the proper payload. 1576 * @property firedWith 1577 * @type Array 1578 */ 1579 this.firedWith = null; 1580 1581 /** 1582 * Custom events support two styles of arguments provided to the event 1583 * subscribers. 1584 * <ul> 1585 * <li>YAHOO.util.CustomEvent.LIST: 1586 * <ul> 1587 * <li>param1: event name</li> 1588 * <li>param2: array of arguments sent to fire</li> 1589 * <li>param3: <optional> a custom object supplied by the subscriber</li> 1590 * </ul> 1591 * </li> 1592 * <li>YAHOO.util.CustomEvent.FLAT 1593 * <ul> 1594 * <li>param1: the first argument passed to fire. If you need to 1595 * pass multiple parameters, use and array or object literal</li> 1596 * <li>param2: <optional> a custom object supplied by the subscriber</li> 1597 * </ul> 1598 * </li> 1599 * </ul> 1600 * @property signature 1601 * @type int 1602 */ 1603 this.signature = signature || YAHOO.util.CustomEvent.LIST; 1604 1605 /** 1606 * The subscribers to this event 1607 * @property subscribers 1608 * @type Subscriber[] 1609 */ 1610 this.subscribers = []; 1611 1612 if (!this.silent) { 1613 YAHOO.log( "Creating " + this, "info", "Event" ); 1614 } 1615 1616 var onsubscribeType = "_YUICEOnSubscribe"; 1617 1618 // Only add subscribe events for events that are not generated by 1619 // CustomEvent 1620 if (type !== onsubscribeType) { 1621 1622 /** 1623 * Custom events provide a custom event that fires whenever there is 1624 * a new subscriber to the event. This provides an opportunity to 1625 * handle the case where there is a non-repeating event that has 1626 * already fired has a new subscriber. 1627 * 1628 * @event subscribeEvent 1629 * @type YAHOO.util.CustomEvent 1630 * @param fn {Function} The function to execute 1631 * @param obj <Object> An object to be passed along when the event fires. 1632 * Defaults to the custom event. 1633 * @param override <boolean|Object> If true, the obj passed in becomes the 1634 * execution context of the listener. If an object, that object becomes 1635 * the execution context. Defaults to the custom event. 1636 */ 1637 this.subscribeEvent = 1638 new YAHOO.util.CustomEvent(onsubscribeType, this, true); 1639 1640 } 1641 1642 1643 /** 1644 * In order to make it possible to execute the rest of the subscriber 1645 * stack when one thows an exception, the subscribers exceptions are 1646 * caught. The most recent exception is stored in this property 1647 * @property lastError 1648 * @type Error 1649 */ 1650 this.lastError = null; 1651}; 1652 1653/** 1654 * Subscriber listener sigature constant. The LIST type returns three 1655 * parameters: the event type, the array of args passed to fire, and 1656 * the optional custom object 1657 * @property YAHOO.util.CustomEvent.LIST 1658 * @static 1659 * @type int 1660 */ 1661YAHOO.util.CustomEvent.LIST = 0; 1662 1663/** 1664 * Subscriber listener sigature constant. The FLAT type returns two 1665 * parameters: the first argument passed to fire and the optional 1666 * custom object 1667 * @property YAHOO.util.CustomEvent.FLAT 1668 * @static 1669 * @type int 1670 */ 1671YAHOO.util.CustomEvent.FLAT = 1; 1672 1673YAHOO.util.CustomEvent.prototype = { 1674 1675 /** 1676 * Subscribes the caller to this event 1677 * @method subscribe 1678 * @param {Function} fn The function to execute 1679 * @param {Object} obj An object to be passed along when the event 1680 * fires. 1681 * @param {boolean|Object} overrideContext If true, the obj passed in 1682 * becomes the execution. 1683 * context of the listener. If an object, that object becomes the execution 1684 * context. 1685 */ 1686 subscribe: function(fn, obj, overrideContext) { 1687 1688 if (!fn) { 1689throw new Error("Invalid callback for subscriber to '" + this.type + "'"); 1690 } 1691 1692 if (this.subscribeEvent) { 1693 this.subscribeEvent.fire(fn, obj, overrideContext); 1694 } 1695 1696 var s = new YAHOO.util.Subscriber(fn, obj, overrideContext); 1697 1698 if (this.fireOnce && this.fired) { 1699 this.notify(s, this.firedWith); 1700 } else { 1701 this.subscribers.push(s); 1702 } 1703 }, 1704 1705 /** 1706 * Unsubscribes subscribers. 1707 * @method unsubscribe 1708 * @param {Function} fn The subscribed function to remove, if not supplied 1709 * all will be removed 1710 * @param {Object} obj The custom object passed to subscribe. This is 1711 * optional, but if supplied will be used to 1712 * disambiguate multiple listeners that are the same 1713 * (e.g., you subscribe many object using a function 1714 * that lives on the prototype) 1715 * @return {boolean} True if the subscriber was found and detached. 1716 */ 1717 unsubscribe: function(fn, obj) { 1718 1719 if (!fn) { 1720 return this.unsubscribeAll(); 1721 } 1722 1723 var found = false;
1724 for (var i=0, len=this.subscribers.length; i<len; ++i) { 1725 var s = this.subscribers[i]; 1726 if (s && s.contains(fn, obj)) { 1727 this._delete(i); 1728 found = true; 1729 } 1730 } 1731 1732 return found; 1733 }, 1734 1735 /** 1736 * Notifies the subscribers. The callback functions will be executed 1737 * from the context specified when the event was created, and with the 1738 * following parameters: 1739 * <ul> 1740 * <li>The type of event</li> 1741 * <li>All of the arguments fire() was executed with as an array</li> 1742 * <li>The custom object (if any) that was passed into the subscribe() 1743 * method</li> 1744 * </ul> 1745 * @method fire 1746 * @param {Object*} arguments an arbitrary set of parameters to pass to 1747 * the handler. 1748 * @return {boolean} false if one of the subscribers returned false, 1749 * true otherwise 1750 */ 1751 fire: function() { 1752 1753 this.lastError = null; 1754 1755 var errors = [], 1756 len=this.subscribers.length; 1757 1758 1759 var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false; 1760 1761 if (this.fireOnce) { 1762 if (this.fired) { 1763 YAHOO.log('fireOnce event has already fired: ' + this.type); 1764 return true; 1765 } else { 1766 this.firedWith = args; 1767 } 1768 } 1769 1770 this.fired = true; 1771 1772 if (!len && this.silent) { 1773 //YAHOO.log('DEBUG no subscribers'); 1774 return true; 1775 } 1776 1777 if (!this.silent) { 1778 YAHOO.log( "Firing " + this + ", " + 1779 "args: " + args + ", " + 1780 "subscribers: " + len, 1781 "info", "Event" ); 1782 } 1783 1784 // make a copy of the subscribers so that there are 1785 // no index problems if one subscriber removes another. 1786 var subs = this.subscribers.slice(); 1787 1788 for (i=0; i<len; ++i) { 1789 var s = subs[i]; 1790 if (!s || !s.fn) { 1791 rebuild=true; 1792 } else { 1793 1794 ret = this.notify(s, args); 1795 1796 if (false === ret) { 1797 if (!this.silent) { 1798 YAHOO.log("Event stopped, sub " + i + " of " + len, "info", "Event"); 1799 } 1800 1801 break; 1802 } 1803 } 1804 } 1805 1806 return (ret !== false); 1807 }, 1808 1809 notify: function(s, args) { 1810 1811 var ret, param=null, scope = s.getScope(this.scope), 1812 throwErrors = YAHOO.util.Event.throwErrors; 1813 1814 if (!this.silent) { 1815 YAHOO.log( this.type + "-> " + s, "info", "Event" ); 1816 } 1817 1818 if (this.signature == YAHOO.util.CustomEvent.FLAT) { 1819 1820 if (args.length > 0) { 1821 param = args[0]; 1822 } 1823 1824 try { 1825 ret = s.fn.call(scope, param, s.obj); 1826 } catch(e) { 1827 this.lastError = e; 1828 // errors.push(e); 1829 YAHOO.log(this + " subscriber exception: " + e, "error", "Event"); 1830 if (throwErrors) { 1831 throw e; 1832 } 1833 } 1834 } else { 1835 try { 1836 ret = s.fn.call(scope, this.type, args, s.obj); 1837 } catch(ex) { 1838 this.lastError = ex; 1839 YAHOO.log(this + " subscriber exception: " + ex, "error", "Event"); 1840 if (throwErrors) { 1841 throw ex; 1842 } 1843 } 1844 } 1845 1846 return ret; 1847 }, 1848 1849 /** 1850 * Removes all listeners 1851 * @method unsubscribeAll 1852 * @return {int} The number of listeners unsubscribed 1853 */ 1854 unsubscribeAll: function() { 1855 var l = this.subscribers.length, i; 1856 for (i=l-1; i>-1; i--) { 1857 this._delete(i); 1858 } 1859 1860 this.subscribers=[]; 1861 1862 return l; 1863 }, 1864 1865 /** 1866 * @method _delete 1867 * @private 1868 */ 1869 _delete: function(index) { 1870 var s = this.subscribers[index]; 1871 if (s) { 1872 delete s.fn; 1873 delete s.obj; 1874 } 1875 1876 // this.subscribers[index]=null; 1877 this.subscribers.splice(index, 1); 1878 }, 1879 1880 /** 1881 * @method toString 1882 */ 1883 toString: function() { 1884 return "CustomEvent: " + "'" + this.type + "', " + 1885 "context: " + this.scope; 1886 1887 } 1888}; 1889 1890///////////////////////////////////////////////////////////////////// 1891 1892/** 1893 * Stores the subscriber information to be used when the event fires. 1894 * @param {Function} fn The function to execute 1895 * @param {Object} obj An object to be passed along when the event fires 1896 * @param {boolean} overrideContext If true, the obj passed in becomes the execution 1897 * context of the listener 1898 * @class Subscriber 1899 * @constructor 1900 */ 1901YAHOO.util.Subscriber = function(fn, obj, overrideContext) { 1902 1903 /** 1904 * The callback that will be execute when the event fires 1905 * @property fn 1906 * @type function 1907 */ 1908 this.fn = fn; 1909 1910 /** 1911 * An optional custom object that will passed to the callback when 1912 * the event fires 1913 * @property obj 1914 * @type object 1915 */ 1916 this.obj = YAHOO.lang.isUndefined(obj) ? null : obj; 1917 1918 /** 1919 * The default execution context for the event listener is defined when the 1920 * event is created (usually the object which contains the event). 1921 * By setting overrideContext to true, the execution context becomes the custom 1922 * object passed in by the subscriber. If overrideContext is an object, that 1923 * object becomes the context. 1924 * @property overrideContext 1925 * @type boolean|object 1926 */ 1927 this.overrideContext = overrideContext; 1928 1929}; 1930 1931/** 1932 * Returns the execution context for this listener. If overrideContext was set to true 1933 * the custom obj will be the context. If overrideContext is an object, that is the 1934 * context, otherwise the default context will be used. 1935 * @method getScope 1936 * @param {Object} defaultScope the context to use if this listener does not 1937 * override it. 1938 */ 1939YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) { 1940 if (this.overrideContext) { 1941 if (this.overrideContext === true) { 1942 return this.obj; 1943 } else { 1944 return this.overrideContext; 1945 } 1946 } 1947 return defaultScope; 1948}; 1949 1950/** 1951 * Returns true if the fn and obj match this objects properties. 1952 * Used by the unsubscribe method to match the right subscriber. 1953 * 1954 * @method contains 1955 * @param {Function} fn the function to execute 1956 * @param {Object} obj an object to be passed along when the event fires 1957 * @return {boolean} true if the supplied arguments match this 1958 * subscriber's signature. 1959 */ 1960YAHOO.util.Subscriber.prototype.contains = function(fn, obj) { 1961 if (obj) { 1962 return (this.fn == fn && this.obj == obj); 1963 } else { 1964 return (this.fn == fn); 1965 } 1966}; 1967 1968/** 1969 * @method toString 1970 */ 1971YAHOO.util.Subscriber.prototype.toString = function() { 1972 return "Subscriber { obj: " + this.obj + 1973 ", overrideContext: " + (this.overrideContext || "no") + " }"; 1974}; 1975 1976/** 1977 * The Event Utility provides utilities for managing DOM Events and tools 1978 * for building event systems 1979 * 1980 * @module event 1981 * @title Event Utility 1982 * @namespace YAHOO.util 1983 * @requires yahoo 1984 */ 1985 1986// The first instance of Event will win if it is loaded more than once. 1987// @TODO this needs to be changed so that only the state data that needs to 1988// be preserved is kept, while methods are overwritten/added as
1988needed. 1989// This means that the module pattern can't be used. 1990if (!YAHOO.util.Event) { 1991 1992/** 1993 * The event utility provides functions to add and remove event listeners, 1994 * event cleansing. It also tries to automatically remove listeners it 1995 * registers during the unload event. 1996 * 1997 * @class Event 1998 * @static 1999 */ 2000 YAHOO.util.Event = function() { 2001 2002 /** 2003 * True after the onload event has fired 2004 * @property loadComplete 2005 * @type boolean 2006 * @static 2007 * @private 2008 */ 2009 var loadComplete = false, 2010 2011 /** 2012 * Cache of wrapped listeners 2013 * @property listeners 2014 * @type array 2015 * @static 2016 * @private 2017 */ 2018 listeners = [], 2019 2020 2021 /** 2022 * User-defined unload function that will be fired before all events 2023 * are detached 2024 * @property unloadListeners 2025 * @type array 2026 * @static 2027 * @private 2028 */ 2029 unloadListeners = [], 2030 2031 /** 2032 * The number of times to poll after window.onload. This number is 2033 * increased if additional late-bound handlers are requested after 2034 * the page load. 2035 * @property retryCount 2036 * @static 2037 * @private 2038 */ 2039 retryCount = 0, 2040 2041 /** 2042 * onAvailable listeners 2043 * @property onAvailStack 2044 * @static 2045 * @private 2046 */ 2047 onAvailStack = [], 2048 2049 /** 2050 * Counter for auto id generation 2051 * @property counter 2052 * @static 2053 * @private 2054 */ 2055 counter = 0, 2056 2057 /** 2058 * Normalized keycodes for webkit/safari 2059 * @property webkitKeymap 2060 * @type {int: int} 2061 * @private 2062 * @static 2063 * @final 2064 */ 2065 webkitKeymap = { 2066 63232: 38, // up 2067 63233: 40, // down 2068 63234: 37, // left 2069 63235: 39, // right 2070 63276: 33, // page up 2071 63277: 34, // page down 2072 25: 9 // SHIFT-TAB (Safari provides a different key code in 2073 // this case, even though the shiftKey modifier is set) 2074 }, 2075 2076 isIE = YAHOO.env.ua.ie, 2077 2078 // String constants used by the addFocusListener and removeFocusListener methods 2079 2080 FOCUSIN = "focusin", 2081 FOCUSOUT = "focusout"; 2082 2083 return { 2084 2085 /** 2086 * The number of times we should look for elements that are not 2087 * in the DOM at the time the event is requested after the document 2088 * has been loaded. The default is 500@amp;40 ms, so it will poll 2089 * for 20 seconds or until all outstanding handlers are bound 2090 * (whichever comes first). 2091 * @property POLL_RETRYS 2092 * @type int 2093 * @static 2094 * @final 2095 */ 2096 POLL_RETRYS: 500, 2097 2098 /** 2099 * The poll interval in milliseconds 2100 * @property POLL_INTERVAL 2101 * @type int 2102 * @static 2103 * @final 2104 */ 2105 POLL_INTERVAL: 40, 2106 2107 /** 2108 * Element to bind, int constant 2109 * @property EL 2110 * @type int 2111 * @static 2112 * @final 2113 */ 2114 EL: 0, 2115 2116 /** 2117 * Type of event, int constant 2118 * @property TYPE 2119 * @type int 2120 * @static 2121 * @final 2122 */ 2123 TYPE: 1, 2124 2125 /** 2126 * Function to execute, int constant 2127 * @property FN 2128 * @type int 2129 * @static 2130 * @final 2131 */ 2132 FN: 2, 2133 2134 /** 2135 * Function wrapped for context correction and cleanup, int constant 2136 * @property WFN 2137 * @type int 2138 * @static 2139 * @final 2140 */ 2141 WFN: 3, 2142 2143 /** 2144 * Object passed in by the user that will be returned as a 2145 * parameter to the callback, int constant. Specific to 2146 * unload listeners 2147 * @property OBJ 2148 * @type int 2149 * @static 2150 * @final 2151 */ 2152 UNLOAD_OBJ: 3, 2153 2154 /** 2155 * Adjusted context, either the element we are registering the event 2156 * on or the custom object passed in by the listener, int constant 2157 * @property ADJ_SCOPE 2158 * @type int 2159 * @static 2160 * @final 2161 */ 2162 ADJ_SCOPE: 4, 2163 2164 /** 2165 * The original obj passed into addListener 2166 * @property OBJ 2167 * @type int 2168 * @static 2169 * @final 2170 */ 2171 OBJ: 5, 2172 2173 /** 2174 * The original context parameter passed into addListener 2175 * @property OVERRIDE 2176 * @type int 2177 * @static 2178 * @final 2179 */ 2180 OVERRIDE: 6, 2181 2182 /** 2183 * The original capture parameter passed into addListener 2184 * @property CAPTURE 2185 * @type int 2186 * @static 2187 * @final 2188 */ 2189 CAPTURE: 7, 2190 2191 /** 2192 * addListener/removeListener can throw errors in unexpected scenarios. 2193 * These errors are suppressed, the method returns false, and this property 2194 * is set 2195 * @property lastError 2196 * @static 2197 * @type Error 2198 */ 2199 lastError: null, 2200 2201 /** 2202 * Safari detection 2203 * @property isSafari 2204 * @private 2205 * @static 2206 * @deprecated use YAHOO.env.ua.webkit 2207 */ 2208 isSafari: YAHOO.env.ua.webkit, 2209 2210 /** 2211 * webkit version 2212 * @property webkit 2213 * @type string 2214 * @private 2215 * @static 2216 * @deprecated use YAHOO.env.ua.webkit 2217 */ 2218 webkit: YAHOO.env.ua.webkit, 2219 2220 /** 2221 * IE detection 2222 * @property isIE 2223 * @private 2224 * @static 2225 * @deprecated use YAHOO.env.ua.ie 2226 */ 2227 isIE: isIE, 2228 2229 /** 2230 * poll handle 2231 * @property _interval 2232 * @static 2233 * @private 2234 */ 2235 _interval: null, 2236 2237 /** 2238 * document readystate poll handle 2239 * @property _dri 2240 * @static 2241 * @private 2242 */ 2243 _dri: null, 2244 2245 2246 /** 2247 * Map of special event types 2248 * @property _specialTypes 2249 * @static 2250 * @private 2251 */ 2252 _specialTypes: { 2253 focusin: (isIE ? "focusin" : "focus"), 2254 focusout: (isIE ? "focusout" : "blur") 2255 }, 2256 2257 2258 /** 2259 * True when the document is initially usable 2260 * @property DOMReady 2261 * @type boolean 2262 * @static 2263 */ 2264 DOMReady: false, 2265 2266 /** 2267 * Errors thrown by subscribers of custom events are caught 2268 * and the error message is written to the debug console. If 2269 * this property is set to true, it will also re-throw the 2270 * error. 2271 * @property throwErrors 2272 * @type boolean 2273 * @default false 2274 */ 2275 throwErrors: false, 2276 2277 2278 /** 2279 * @method startInterval 2280 * @static 2281 * @private 2282 */ 2283 startInterval: function() { 2284 if (!this._interval) { 2285 // var self = this; 2286 // var callback = function() { self._tryPreloadAttach(); }; 2287 // this._interval = setInterval(callback, this.POLL_INTERVAL); 2288 this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true); 2289 } 2290 }, 2291 2292 /** 2293 * Executes the supplied callback when the item with the supplied 2294 * id is found. This is meant to be used to execute behavior as 2295 * soon as possible as the page loads. If you use this after the 2296 * initial page load it will poll for a fixed time for the element. 2297 * The number of times it will poll and the frequency are 2298 * configurable. By default it will poll for 10 seconds. 2299 * 2300 * <p>The callback is executed with a single parameter:
2301 * the custom object parameter, if provided.</p> 2302 * 2303 * @method onAvailable 2304 * 2305 * @param {string||string[]} id the id of the element, or an array 2306 * of ids to look for. 2307 * @param {function} fn what to execute when the element is found. 2308 * @param {object} obj an optional object to be passed back as 2309 * a parameter to fn. 2310 * @param {boolean|object} overrideContext If set to true, fn will execute 2311 * in the context of obj, if set to an object it 2312 * will execute in the context of that object 2313 * @param checkContent {boolean} check child node readiness (onContentReady) 2314 * @static 2315 */ 2316 onAvailable: function(id, fn, obj, overrideContext, checkContent) { 2317 2318 var a = (YAHOO.lang.isString(id)) ? [id] : id; 2319 2320 for (var i=0; i<a.length; i=i+1) { 2321 onAvailStack.push({id: a[i], 2322 fn: fn, 2323 obj: obj, 2324 overrideContext: overrideContext, 2325 checkReady: checkContent }); 2326 } 2327 2328 retryCount = this.POLL_RETRYS; 2329 2330 this.startInterval(); 2331 }, 2332 2333 /** 2334 * Works the same way as onAvailable, but additionally checks the 2335 * state of sibling elements to determine if the content of the 2336 * available element is safe to modify. 2337 * 2338 * <p>The callback is executed with a single parameter: 2339 * the custom object parameter, if provided.</p> 2340 * 2341 * @method onContentReady 2342 * 2343 * @param {string} id the id of the element to look for. 2344 * @param {function} fn what to execute when the element is ready. 2345 * @param {object} obj an optional object to be passed back as 2346 * a parameter to fn. 2347 * @param {boolean|object} overrideContext If set to true, fn will execute 2348 * in the context of obj. If an object, fn will 2349 * exectute in the context of that object 2350 * 2351 * @static 2352 */ 2353 onContentReady: function(id, fn, obj, overrideContext) { 2354 this.onAvailable(id, fn, obj, overrideContext, true); 2355 }, 2356 2357 /** 2358 * Executes the supplied callback when the DOM is first usable. This 2359 * will execute immediately if called after the DOMReady event has 2360 * fired. @todo the DOMContentReady event does not fire when the 2361 * script is dynamically injected into the page. This means the 2362 * DOMReady custom event will never fire in FireFox or Opera when the 2363 * library is injected. It _will_ fire in Safari, and the IE 2364 * implementation would allow for us to fire it if the defered script 2365 * is not available. We want this to behave the same in all browsers. 2366 * Is there a way to identify when the script has been injected 2367 * instead of included inline? Is there a way to know whether the 2368 * window onload event has fired without having had a listener attached 2369 * to it when it did so? 2370 * 2371 * <p>The callback is a CustomEvent, so the signature is:</p> 2372 * <p>type <string>, args <array>, customobject <object></p> 2373 * <p>For DOMReady events, there are no fire argments, so the 2374 * signature is:</p> 2375 * <p>"DOMReady", [], obj</p> 2376 * 2377 * 2378 * @method onDOMReady 2379 * 2380 * @param {function} fn what to execute when the element is found. 2381 * @param {object} obj an optional object to be passed back as 2382 * a parameter to fn. 2383 * @param {boolean|object} overrideContext If set to true, fn will execute 2384 * in the context of obj, if set to an object it 2385 * will execute in the context of that object 2386 * 2387 * @static 2388 */ 2389 // onDOMReady: function(fn, obj, overrideContext) { 2390 onDOMReady: function() { 2391 this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments); 2392 }, 2393 2394 2395 /** 2396 * Appends an event handler 2397 * 2398 * @method _addListener 2399 * 2400 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2401 * reference, or a collection of ids and/or elements to assign the 2402 * listener to. 2403 * @param {String} sType The type of event to append 2404 * @param {Function} fn The method the event invokes 2405 * @param {Object} obj An arbitrary object that will be 2406 * passed as a parameter to the handler 2407 * @param {Boolean|object} overrideContext If true, the obj passed in becomes 2408 * the execution context of the listener. If an 2409 * object, this object becomes the execution 2410 * context. 2411 * @param {boolen} capture capture or bubble phase 2412 * @return {Boolean} True if the action was successful or defered, 2413 * false if one or more of the elements 2414 * could not have the listener attached, 2415 * or if the operation throws an exception. 2416 * @private 2417 * @static 2418 */ 2419 _addListener: function(el, sType, fn, obj, overrideContext, bCapture) { 2420 2421 if (!fn || !fn.call) { 2422 YAHOO.log(sType + " addListener failed, invalid callback", "error", "Event"); 2423 return false;
2424 } 2425 2426 // The el argument can be an array of elements or element ids. 2427 if ( this._isValidCollection(el)) { 2428 var ok = true; 2429 for (var i=0,len=el.length; i<len; ++i) { 2430 ok = this.on(el[i], 2431 sType, 2432 fn, 2433 obj, 2434 overrideContext) && ok; 2435 } 2436 return ok; 2437 2438 } else if (YAHOO.lang.isString(el)) { 2439 var oEl = this.getEl(el); 2440 // If the el argument is a string, we assume it is 2441 // actually the id of the element. If the page is loaded 2442 // we convert el to the actual element, otherwise we 2443 // defer attaching the event until onload event fires 2444 2445 // check to see if we need to delay hooking up the event 2446 // until after the page loads. 2447 if (oEl) { 2448 el = oEl; 2449 } else { 2450 // defer adding the event until the element is available 2451 this.onAvailable(el, function() { 2452 YAHOO.util.Event._addListener(el, sType, fn, obj, overrideContext, bCapture); 2453 }); 2454 2455 return true; 2456 } 2457 } 2458 2459 // Element should be an html element or an array if we get 2460 // here. 2461 if (!el) { 2462 // this.logger.debug("unable to attach event " + sType); 2463 return false; 2464 } 2465 2466 // we need to make sure we fire registered unload events 2467 // prior to automatically unhooking them. So we hang on to 2468 // these instead of attaching them to the window and fire the 2469 // handles explicitly during our one unload event. 2470 if ("unload" == sType && obj !== this) { 2471 unloadListeners[unloadListeners.length] = 2472 [el, sType, fn, obj, overrideContext]; 2473 return true; 2474 } 2475 2476 // this.logger.debug("Adding handler: " + el + ", " + sType); 2477 2478 // if the user chooses to override the context, we use the custom 2479 // object passed in, otherwise the executing context will be the 2480 // HTML element that the event is registered on 2481 var context = el; 2482 if (overrideContext) { 2483 if (overrideContext === true) { 2484 context = obj; 2485 } else { 2486 context = overrideContext; 2487 } 2488 } 2489 2490 // wrap the function so we can return the obj object when 2491 // the event fires; 2492 var wrappedFn = function(e) { 2493 return fn.call(context, YAHOO.util.Event.getEvent(e, el), 2494 obj); 2495 }; 2496 2497 var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture]; 2498 var index = listeners.length; 2499 // cache the listener so we can try to automatically unload 2500 listeners[index] = li; 2501 2502 try { 2503 this._simpleAdd(el, sType, wrappedFn, bCapture); 2504 } catch(ex) { 2505 // handle an error trying to attach an event. If it fails 2506 // we need to clean up the cache 2507 this.lastError = ex; 2508 this.removeListener(el, sType, fn); 2509 return false; 2510 } 2511 2512 return true; 2513 2514 }, 2515 2516 /** 2517 * Checks to see if the type requested is a special type 2518 * (as defined by the _specialTypes hash), and (if so) returns 2519 * the special type name. 2520 * 2521 * @method _getType 2522 * 2523 * @param {String} sType The type to look up 2524 * @private 2525 */ 2526 _getType: function (type) { 2527 2528 return this._specialTypes[type] || type; 2529 2530 }, 2531 2532 2533 /** 2534 * Appends an event handler 2535 * 2536 * @method addListener 2537 * 2538 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2539 * reference, or a collection of ids and/or elements to assign the 2540 * listener to. 2541 * @param {String} sType The type of event to append 2542 * @param {Function} fn The method the event invokes 2543 * @param {Object} obj An arbitrary object that will be 2544 * passed as a parameter to the handler 2545 * @param {Boolean|object} overrideContext If true, the obj passed in becomes 2546 * the execution context of the listener. If an 2547 * object, this object becomes the execution 2548 * context. 2549 * @return {Boolean} True if the action was successful or defered, 2550 * false if one or more of the elements 2551 * could not have the listener attached, 2552 * or if the operation throws an exception. 2553 * @static 2554 */ 2555 addListener: function (el, sType, fn, obj, overrideContext) { 2556 2557 var capture = ((sType == FOCUSIN || sType == FOCUSOUT) && !YAHOO.env.ua.ie) ? true : false;
2558 2559 return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture); 2560 2561 }, 2562 2563 2564 /** 2565 * Attaches a focusin event listener to the specified element for 2566 * the purpose of listening for the focus event on the element's 2567 * descendants. 2568 * @method addFocusListener 2569 * 2570 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2571 * reference, or a collection of ids and/or elements to assign the 2572 * listener to. 2573 * @param {Function} fn The method the event invokes 2574 * @param {Object} obj An arbitrary object that will be 2575 * passed as a parameter to the handler 2576 * @param {Boolean|object} overrideContext If true, the obj passed in becomes 2577 * the execution context of the listener. If an 2578 * object, this object becomes the execution 2579 * context. 2580 * @return {Boolean} True if the action was successful or defered, 2581 * false if one or more of the elements 2582 * could not have the listener attached, 2583 * or if the operation throws an exception. 2584 * @static 2585 * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type. 2586 */ 2587 addFocusListener: function (el, fn, obj, overrideContext) { 2588 return this.on(el, FOCUSIN, fn, obj, overrideContext); 2589 }, 2590 2591 2592 /** 2593 * Removes a focusin event listener to the specified element for 2594 * the purpose of listening for the focus event on the element's 2595 * descendants. 2596 * 2597 * @method removeFocusListener 2598 * 2599 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2600 * reference, or a collection of ids and/or elements to remove 2601 * the listener from. 2602 * @param {Function} fn the method the event invokes. If fn is 2603 * undefined, then all event handlers for the type of event are 2604 * removed. 2605 * @return {boolean} true if the unbind was successful, false 2606 * otherwise. 2607 * @static 2608 * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type. 2609 */ 2610 removeFocusListener: function (el, fn) { 2611 return this.removeListener(el, FOCUSIN, fn); 2612 }, 2613 2614 /** 2615 * Attaches a focusout event listener to the specified element for 2616 * the purpose of listening for the blur event on the element's 2617 * descendants. 2618 * 2619 * @method addBlurListener 2620 * 2621 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2622 * reference, or a collection of ids and/or elements to assign the 2623 * listener to. 2624 * @param {Function} fn The method the event invokes 2625 * @param {Object} obj An arbitrary object that will be 2626 * passed as a parameter to the handler 2627 * @param {Boolean|object} overrideContext If true, the obj passed in becomes 2628 * the execution context of the listener. If an 2629 * object, this object becomes the execution 2630 * context. 2631 * @return {Boolean} True if the action was successful or defered, 2632 * false if one or more of the elements 2633 * could not have the listener attached, 2634 * or if the operation throws an exception. 2635 * @static 2636 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type. 2637 */ 2638 addBlurListener: function (el, fn, obj, overrideContext) { 2639 return this.on(el, FOCUSOUT, fn, obj, overrideContext); 2640 }, 2641 2642 /** 2643 * Removes a focusout event listener to the specified element for 2644 * the purpose of listening for the blur event on the element's 2645 * descendants. 2646 * 2647 * @method removeBlurListener 2648 * 2649 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2650 * reference, or a collection of ids and/or elements to remove 2651 * the listener from. 2652 * @param {Function} fn the method the event invokes. If fn is 2653 * undefined, then all event handlers for the type of event are 2654 * removed. 2655 * @return {boolean} true if the unbind was successful, false 2656 * otherwise. 2657 * @static 2658 * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type. 2659 */ 2660 removeBlurListener: function (el, fn) { 2661 return this.removeListener(el, FOCUSOUT, fn); 2662 }, 2663 2664 /** 2665 * Removes an event listener 2666 * 2667 * @method removeListener 2668 * 2669 * @param {String|HTMLElement|Array|NodeList} el An id, an element 2670 * reference, or a collection of ids and/or elements to remove 2671 * the listener from. 2672 * @param {String} sType the type of event to remove. 2673 * @param {Function} fn the method the event invokes. If fn is 2674 * undefined, then all event handlers for the type of event are 2675 * removed. 2676 * @return {boolean} true if the unbind was successful, false 2677 * otherwise. 2678 * @static 2679 */ 2680 removeListener: function(el, sType, fn) { 2681 var i, len, li; 2682 2683 sType = this._getType(sType); 2684 2685 // The el argument can be a string 2686 if (typeof el == "string") { 2687 el = this.getEl(el); 2688 // The el argument can be an array of elements or element ids. 2689 } else if ( this._isValidCollection(el)) { 2690 var ok = true; 2691 for (i=el.length-1; i>-1; i--) { 2692 ok = ( this.removeListener(el[i], sType, fn) && ok ); 2693 } 2694 return ok; 2695 } 2696 2697 if (!fn || !fn.call) { 2698 // this.logger.debug("Error, function is not val
2698id " + fn); 2699 //return false; 2700 return this.purgeElement(el, false, sType); 2701 } 2702 2703 if ("unload" == sType) { 2704 2705 for (i=unloadListeners.length-1; i>-1; i--) { 2706 li = unloadListeners[i]; 2707 if (li && 2708 li[0] == el && 2709 li[1] == sType && 2710 li[2] == fn) { 2711 unloadListeners.splice(i, 1); 2712 // unloadListeners[i]=null; 2713 return true; 2714 } 2715 } 2716 2717 return false; 2718 } 2719 2720 var cacheItem = null; 2721 2722 // The index is a hidden parameter; needed to remove it from 2723 // the method signature because it was tempting users to 2724 // try and take advantage of it, which is not possible. 2725 var index = arguments[3]; 2726 2727 if ("undefined" === typeof index) { 2728 index = this._getCacheIndex(listeners, el, sType, fn); 2729 } 2730 2731 if (index >= 0) { 2732 cacheItem = listeners[index]; 2733 } 2734 2735 if (!el || !cacheItem) { 2736 // this.logger.debug("cached listener not found"); 2737 return false; 2738 } 2739 2740 // this.logger.debug("Removing handler: " + el + ", " + sType); 2741 2742 var bCapture = cacheItem[this.CAPTURE] === true ? true : false; 2743 2744 try { 2745 this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture); 2746 } catch(ex) { 2747 this.lastError = ex; 2748 return false; 2749 } 2750 2751 // removed the wrapped handler 2752 delete listeners[index][this.WFN]; 2753 delete listeners[index][this.FN]; 2754 listeners.splice(index, 1); 2755 // listeners[index]=null; 2756 2757 return true; 2758 2759 }, 2760 2761 /** 2762 * Returns the event's target element. Safari sometimes provides 2763 * a text node, and this is automatically resolved to the text 2764 * node's parent so that it behaves like other browsers. 2765 * @method getTarget 2766 * @param {Event} ev the event 2767 * @param {boolean} resolveTextNode when set to true the target's 2768 * parent will be returned if the target is a 2769 * text node. @deprecated, the text node is 2770 * now resolved automatically 2771 * @return {HTMLElement} the event's target 2772 * @static 2773 */ 2774 getTarget: function(ev, resolveTextNode) { 2775 var t = ev.target || ev.srcElement; 2776 return this.resolveTextNode(t); 2777 }, 2778 2779 /** 2780 * In some cases, some browsers will return a text node inside 2781 * the actual element that was targeted. This normalizes the 2782 * return value for getTarget and getRelatedTarget. 2783 * 2784 * If accessing a property of the node throws an error, this is 2785 * probably the anonymous div wrapper Gecko adds inside text 2786 * nodes. This likely will only occur when attempting to access 2787 * the relatedTarget. In this case, we now return null because 2788 * the anonymous div is completely useless and we do not know 2789 * what the related target was because we can't even get to 2790 * the element's parent node. 2791 * 2792 * @method resolveTextNode 2793 * @param {HTMLElement} node node to resolve 2794 * @return {HTMLElement} the normized node 2795 * @static 2796 */ 2797 resolveTextNode: function(n) { 2798 try { 2799 if (n && 3 == n.nodeType) { 2800 return n.parentNode; 2801 } 2802 } catch(e) { 2803 return null; 2804 } 2805 2806 return n; 2807 }, 2808 2809 /** 2810 * Returns the event's pageX 2811 * @method getPageX 2812 * @param {Event} ev the event 2813 * @return {int} the event's pageX 2814 * @static 2815 */ 2816 getPageX: function(ev) { 2817 var x = ev.pageX; 2818 if (!x && 0 !== x) { 2819 x = ev.clientX || 0; 2820 2821 if ( this.isIE ) { 2822 x += this._getScrollLeft(); 2823 } 2824 } 2825 2826 return x; 2827 }, 2828 2829 /** 2830 * Returns the event's pageY 2831 * @method getPageY 2832 * @param {Event} ev the event 2833 * @return {int} the event's pageY 2834 * @static 2835 */ 2836 getPageY: function(ev) { 2837 var y = ev.pageY; 2838 if (!y && 0 !== y) { 2839 y = ev.clientY || 0; 2840 2841 if ( this.isIE ) { 2842 y += this._getScrollTop(); 2843 } 2844 } 2845 2846 2847 return y; 2848 }, 2849 2850 /** 2851 * Returns the pageX and pageY properties as an indexed array.
2852 * @method getXY 2853 * @param {Event} ev the event 2854 * @return {[x, y]} the pageX and pageY properties of the event 2855 * @static 2856 */ 2857 getXY: function(ev) { 2858 return [this.getPageX(ev), this.getPageY(ev)]; 2859 }, 2860 2861 /** 2862 * Returns the event's related target 2863 * @method getRelatedTarget 2864 * @param {Event} ev the event 2865 * @return {HTMLElement} the event's relatedTarget 2866 * @static 2867 */ 2868 getRelatedTarget: function(ev) { 2869 var t = ev.relatedTarget; 2870 if (!t) { 2871 if (ev.type == "mouseout") { 2872 t = ev.toElement; 2873 } else if (ev.type == "mouseover") { 2874 t = ev.fromElement; 2875 } 2876 } 2877 2878 return this.resolveTextNode(t); 2879 }, 2880 2881 /** 2882 * Returns the time of the event. If the time is not included, the 2883 * event is modified using the current time. 2884 * @method getTime 2885 * @param {Event} ev the event 2886 * @return {Date} the time of the event 2887 * @static 2888 */ 2889 getTime: function(ev) { 2890 if (!ev.time) { 2891 var t = new Date().getTime(); 2892 try { 2893 ev.time = t; 2894 } catch(ex) { 2895 this.lastError = ex; 2896 return t; 2897 } 2898 } 2899 2900 return ev.time; 2901 }, 2902 2903 /** 2904 * Convenience method for stopPropagation + preventDefault 2905 * @method stopEvent 2906 * @param {Event} ev the event 2907 * @static 2908 */ 2909 stopEvent: function(ev) { 2910 this.stopPropagation(ev); 2911 this.preventDefault(ev); 2912 }, 2913 2914 /** 2915 * Stops event propagation 2916 * @method stopPropagation 2917 * @param {Event} ev the event 2918 * @static 2919 */ 2920 stopPropagation: function(ev) { 2921 if (ev.stopPropagation) { 2922 ev.stopPropagation(); 2923 } else { 2924 ev.cancelBubble = true; 2925 } 2926 }, 2927 2928 /** 2929 * Prevents the default behavior of the event 2930 * @method preventDefault 2931 * @param {Event} ev the event 2932 * @static 2933 */ 2934 preventDefault: function(ev) { 2935 if (ev.preventDefault) { 2936 ev.preventDefault(); 2937 } else { 2938 ev.returnValue = false; 2939 } 2940 }, 2941 2942 /** 2943 * Finds the event in the window object, the caller's arguments, or 2944 * in the arguments of another method in the callstack. This is 2945 * executed automatically for events registered through the event 2946 * manager, so the implementer should not normally need to execute 2947 * this function at all. 2948 * @method getEvent 2949 * @param {Event} e the event parameter from the handler 2950 * @param {HTMLElement} boundEl the element the listener is attached to 2951 * @return {Event} the event 2952 * @static 2953 */ 2954 getEvent: function(e, boundEl) { 2955 var ev = e || window.event; 2956 2957 if (!ev) { 2958 var c = this.getEvent.caller; 2959 while (c) { 2960 ev = c.arguments[0]; 2961 if (ev && Event == ev.constructor) { 2962 break; 2963 } 2964 c = c.caller; 2965 } 2966 } 2967 2968 return ev; 2969 }, 2970 2971 /** 2972 * Returns the charcode for an event 2973 * @method getCharCode 2974 * @param {Event} ev the event 2975 * @return {int} the event's charCode 2976 * @static 2977 */ 2978 getCharCode: function(ev) { 2979 var code = ev.keyCode || ev.charCode || 0; 2980 2981 // webkit key normalization 2982 if (YAHOO.env.ua.webkit && (code in webkitKeymap)) { 2983 code = webkitKeymap[code]; 2984 } 2985 return code; 2986 }, 2987 2988 /** 2989 * Locating the saved event handler data by function ref 2990 * 2991 * @method _getCacheIndex 2992 * @static 2993 * @private 2994 */ 2995 _getCacheIndex: function(a, el, sType, fn) { 2996 for (var i=0, l=a.length; i<l; i=i+1) { 2997 var li = a[i]; 2998 if ( li && 2999 li[this.FN] == fn && 3000 li[this.EL] == el && 3001 li[this.TYPE] == sType ) { 3002 return i; 3003 } 3004 } 3005 3006 return -1; 3007 }, 3008 3009 /** 3010 * Generates an unique ID for the element if it does not already 3011 * have one. 3012 * @method generateId 3013 * @param el the element to create the id for 3014 * @return {string} the resulting id of the element 3015 * @static 3016 */ 3017 generateId: function(el) { 3018 var id = el.id; 3019 3020 if (!id) { 3021 id = "yuievtautoid-" + counter; 3022 ++counter; 3023 el.id = id; 3024 } 3025 3026 return id; 3027 }, 3028 3029 3030 /** 3031 * We want to be able to use getElementsByTagName as a collection 3032 * to attach a group of events to. Unfortunately, different 3033 * browsers return different types of collections. This function 3034 * tests to determine if the object is array-like. It will also 3035 * fail if the object is an array, but is empty. 3036 * @method _isValidCollection 3037 * @param o the object to test 3038 * @return {boolean} true if the object is array-like and populated 3039 * @static 3040 * @private 3041 */ 3042 _isValidCollection: function(o) { 3043 try { 3044 return ( o && // o is something 3045 typeof o !== "string" && // o is not a string 3046 o.length && // o is indexed 3047 !o.tagName && // o is not an HTML element 3048 !o.alert && // o is not a window 3049 typeof o[0] !== "undefined" ); 3050 } catch(ex) { 3051 YAHOO.log("node access error (xframe?)", "warn"); 3052 return false;
3053 } 3054 3055 }, 3056 3057 /** 3058 * @private 3059 * @property elCache 3060 * DOM element cache 3061 * @static 3062 * @deprecated Elements are not cached due to issues that arise when 3063 * elements are removed and re-added 3064 */ 3065 elCache: {}, 3066 3067 /** 3068 * We cache elements bound by id because when the unload event 3069 * fires, we can no longer use document.getElementById 3070 * @method getEl 3071 * @static 3072 * @private 3073 * @deprecated Elements are not cached any longer 3074 */ 3075 getEl: function(id) { 3076 return (typeof id === "string") ? document.getElementById(id) : id; 3077 }, 3078 3079 /** 3080 * Clears the element cache 3081 * @deprecated Elements are not cached any longer 3082 * @method clearCache 3083 * @static 3084 * @private 3085 */ 3086 clearCache: function() { }, 3087 3088 /** 3089 * Custom event the fires when the dom is initially usable 3090 * @event DOMReadyEvent 3091 */ 3092 DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1), 3093 3094 /** 3095 * hook up any deferred listeners 3096 * @method _load 3097 * @static 3098 * @private 3099 */ 3100 _load: function(e) { 3101 3102 if (!loadComplete) { 3103 loadComplete = true; 3104 var EU = YAHOO.util.Event; 3105 3106 // Just in case DOMReady did not go off for some reason 3107 EU._ready(); 3108 3109 // Available elements may not have been detected before the 3110 // window load event fires. Try to find them now so that the 3111 // the user is more likely to get the onAvailable notifications 3112 // before the window load notification 3113 EU._tryPreloadAttach(); 3114 3115 } 3116 }, 3117 3118 /** 3119 * Fires the DOMReady event listeners the first time the document is 3120 * usable. 3121 * @method _ready 3122 * @static 3123 * @private 3124 */ 3125 _ready: function(e) { 3126 var EU = YAHOO.util.Event; 3127 if (!EU.DOMReady) { 3128 EU.DOMReady=true; 3129 3130 // Fire the content ready custom event 3131 EU.DOMReadyEvent.fire(); 3132 3133 // Remove the DOMContentLoaded (FF/Opera) 3134 EU._simpleRemove(document, "DOMContentLoaded", EU._ready); 3135 } 3136 }, 3137 3138 /** 3139 * Polling function that runs before the onload event fires, 3140 * attempting to attach to DOM Nodes as soon as they are 3141 * available 3142 * @method _tryPreloadAttach 3143 * @static 3144 * @private 3145 */ 3146 _tryPreloadAttach: function() { 3147 3148 if (onAvailStack.length === 0) { 3149 retryCount = 0; 3150 if (this._interval) { 3151 // clearInterval(this._interval); 3152 this._interval.cancel(); 3153 this._interval = null; 3154 } 3155 return; 3156 } 3157 3158 if (this.locked) { 3159 return; 3160 } 3161 3162 if (this.isIE) { 3163 // Hold off if DOMReady has not fired and check current 3164 // readyState to protect against the IE operation aborted 3165 // issue. 3166 if (!this.DOMReady) { 3167 this.startInterval(); 3168 return; 3169 } 3170 } 3171 3172 this.locked = true; 3173 3174 // this.logger.debug("tryPreloadAttach"); 3175 3176 // keep trying until after the page is loaded. We need to 3177 // check the page load state prior to trying to bind the 3178 // elements so that we can be certain all elements have been 3179 // tested appropriately 3180 var tryAgain = !loadComplete; 3181 if (!tryAgain) { 3182 tryAgain = (retryCount > 0 && onAvailStack.length > 0); 3183 } 3184 3185 // onAvailable 3186 var notAvail = []; 3187 3188 var executeItem = function (el, item) { 3189 var context = el; 3190 if (item.overrideContext) { 3191 if (item.overrideContext === true) { 3192 context = item.obj; 3193 } else { 3194 context = item.overrideContext; 3195 } 3196 } 3197 item.fn.call(context, item.obj); 3198 }; 3199 3200 var i, len, item, el, ready=[]; 3201 3202 // onAvailable onContentReady 3203 for (i=0, len=onAvailStack.length; i<len; i=i+1) { 3204 item = onAvailStack[i]; 3205 if (item) { 3206 el = this.getEl(item.id); 3207 if (el) { 3208 if (item.checkReady) { 3209 if (loadComplete || el.nextSibling || !tryAgain) { 3210 ready.push(item); 3211 onAvailStack[i] = null; 3212 } 3213 } else { 3214 executeItem(el, item); 3215 onAvailStack[i] = null; 3216 } 3217 } else { 3218 notAvail.push(item); 3219 } 3220 } 3221 } 3222 3223 // make sure onContentReady fires after onAvailable 3224 for (i=0, len=ready.length; i<len; i=i+1) { 3225 item = ready[i]; 3226 executeItem(this.getEl(item.id), item); 3227 } 3228 3229 3230 retryCount--; 3231 3232 if (tryAgain) { 3233 for (i=onAvailStack.length-1; i>-1; i--) { 3234 item = onAvailStack[i]; 3235 if (!item || !item.id) { 3236 onAvailStack.splice(i, 1); 3237 } 3238 } 3239 3240 this.startInterval(); 3241 } else { 3242 if (this._interval) { 3243 // clearInterval(this._interval); 3244 this._interval.cancel(); 3245 this._interval = null; 3246 } 3247 } 3248 3249 this.locked = false;
3250 3251 }, 3252 3253 /** 3254 * Removes all listeners attached to the given element via addListener. 3255 * Optionally, the node's children can also be purged. 3256 * Optionally, you can specify a specific type of event to remove. 3257 * @method purgeElement 3258 * @param {HTMLElement} el the element to purge 3259 * @param {boolean} recurse recursively purge this element's children 3260 * as well. Use with caution. 3261 * @param {string} sType optional type of listener to purge. If 3262 * left out, all listeners will be removed 3263 * @static 3264 */ 3265 purgeElement: function(el, recurse, sType) { 3266 var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el; 3267 var elListeners = this.getListeners(oEl, sType), i, len; 3268 if (elListeners) { 3269 for (i=elListeners.length-1; i>-1; i--) { 3270 var l = elListeners[i]; 3271 this.removeListener(oEl, l.type, l.fn); 3272 } 3273 } 3274 3275 var n = 100; 3276 if (recurse && oEl && oEl.childNodes) { 3277 if (YAHOO.env.ua.ie > 8 || YAHOO.env.ua.ie == 0) { 3278 for (i=0,len=el.childNodes.length; i<len ; ++i) { 3279 this.purgeElement(el.childNodes[i], recurse, sType); 3280 } 3281 } else { 3282 var elements = new Array(); 3283 this.gatherElements(el, elements); 3284 this.purgeElementBatch(elements, n, sType); 3285 } 3286 } 3287 }, 3288 3289 purgeElementBatch: function(elements, n, sType) { 3290 var cycle = 0; 3291 while (elements.length > 0) { 3292 var elListeners = this.getListeners(elements[0], sType); 3293 if (elListeners) { 3294 for (var i=0,len=elListeners.length; i<len ; i++) { 3295 var l = elListeners[i]; 3296 this.removeListener(elements[0], l.type, l.fn); 3297 } 3298 } 3299 elements.shift(); 3300 cycle++; 3301 if (cycle == n) { 3302 var t = setTimeout(function(){YAHOO.util.Event.purgeElementBatch(elements, n, sType);}, 1); 3303 break; 3304 } 3305 } 3306 }, 3307 3308 gatherElements: function(el, elements) { 3309 var nodes = new Array(); 3310 nodes.push(el); 3311 elements.push(el); 3312 var nextNodes = new Array(); 3313 while (nodes.length > 0) { 3314 nextNodes.length = 0; 3315 for (var i=0, len=nodes.length; i<len; i++) { 3316 var curNode = nodes[i]; 3317 if (curNode.childNodes) { 3318 for (var j=0, curLen=curNode.childNodes.length; j<curLen ; j++) { 3319 nextNodes.push(curNode.childNodes[j]); 3320 elements.push(curNode.childNodes[j]); 3321 } 3322 } 3323 } 3324 nodes.length = 0; 3325 nodes = nextNodes.slice(0); 3326 } 3327 }, 3328 3329 /** 3330 * Returns all listeners attached to the given element via addListener. 3331 * Optionally, you can specify a specific type of event to return. 3332 * @method getListeners 3333 * @param el {HTMLElement|string} the element or element id to inspect 3334 * @param sType {string} optional type of listener to return. If 3335 * left out, all listeners will be returned 3336 * @return {Object} the listener. Contains the following fields: 3337 * type: (string) the type of event 3338 * fn: (function) the callback supplied to addListener 3339 * obj: (object) the custom object supplied to addListener 3340 * adjust: (boolean|object) whether or not to adjust the default context 3341 * scope: (boolean) the derived context based on the adjust parameter 3342 * index: (int) its position in the Event util listener cache 3343 * @static 3344 */ 3345 getListeners: function(el, sType) { 3346 var results=[], searchLists; 3347 if (!sType) { 3348 searchLists = [listeners, unloadListeners]; 3349 } else if (sType === "unload") { 3350 searchLists = [unloadListeners]; 3351 } else { 3352 sType = this._getType(sType); 3353 searchLists = [listeners]; 3354 } 3355 3356 var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el; 3357 3358 for (var j=0;j<searchLists.length; j=j+1) { 3359 var searchList = searchLists[j]; 3360 if (searchList) { 3361 for (var i=0,len=searchList.length; i<len ; ++i) { 3362 var l = searchList[i]; 3363 if ( l && l[this.EL] === oEl && 3364 (!sType || sType === l[this.TYPE]) ) { 3365 results.push({ 3366 type: l[this.TYPE], 3367 fn: l[this.FN], 3368 obj: l[this.OBJ], 3369 adjust: l[this.OVERRIDE], 3370 scope: l[this.ADJ_SCOPE], 3371 index: i 3372 }); 3373 } 3374 } 3375 } 3376 } 3377 3378 return (results.length) ? results : null; 3379 }, 3380 3381 /** 3382 * Removes all listeners registered by pe.event. Called 3383 * automatically during the unload event. 3384 * @method _unload 3385 * @static 3386 * @private 3387 */ 3388 _unload: function(e) { 3389 3390 var EU = YAHOO.util.Event, i, j, l, len, index, 3391 ul = unloadListeners.slice(), context; 3392 3393 // execute and clear stored unload listeners 3394 for (i=0, len=unloadListeners.length; i<len; ++i) { 3395 l = ul[i]; 3396 if (l) { 3397 try { 3398 context = window; 3399 if (l[EU.ADJ_SCOPE]) { 3400 if (l[EU.ADJ_SCOPE] === true) { 3401 context = l[EU.UNLOAD_OBJ]; 3402 } else { 3403 context = l[EU.ADJ_SCOPE]; 3404 } 3405 } 3406 l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]
3406), l[EU.UNLOAD_OBJ] ); 3407 } catch(e1) {} 3408 ul[i] = null; 3409 } 3410 } 3411 3412 l = null; 3413 context = null; 3414 unloadListeners = null; 3415 3416 // Remove listeners to handle IE memory leaks 3417 // 2.5.0 listeners are removed for all browsers again. FireFox preserves 3418 // at least some listeners between page refreshes, potentially causing 3419 // errors during page load (mouseover listeners firing before they 3420 // should if the user moves the mouse at the correct moment). 3421 if (listeners) { 3422 for (j=listeners.length-1; j>-1; j--) { 3423 l = listeners[j]; 3424 if (l) { 3425 try { 3426 EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j); 3427 } catch(e2) {} 3428 } 3429 } 3430 l=null; 3431 } 3432 3433 try { 3434 EU._simpleRemove(window, "unload", EU._unload); 3435 EU._simpleRemove(window, "load", EU._load); 3436 } catch(e3) {} 3437 3438 }, 3439 3440 /** 3441 * Returns scrollLeft 3442 * @method _getScrollLeft 3443 * @static 3444 * @private 3445 */ 3446 _getScrollLeft: function() { 3447 return this._getScroll()[1]; 3448 }, 3449 3450 /** 3451 * Returns scrollTop 3452 * @method _getScrollTop 3453 * @static 3454 * @private 3455 */ 3456 _getScrollTop: function() { 3457 return this._getScroll()[0]; 3458 }, 3459 3460 /** 3461 * Returns the scrollTop and scrollLeft. Used to calculate the 3462 * pageX and pageY in Internet Explorer 3463 * @method _getScroll 3464 * @static 3465 * @private 3466 */ 3467 _getScroll: function() { 3468 var dd = document.documentElement, db = document.body; 3469 if (dd && (dd.scrollTop || dd.scrollLeft)) { 3470 return [dd.scrollTop, dd.scrollLeft]; 3471 } else if (db) { 3472 return [db.scrollTop, db.scrollLeft]; 3473 } else { 3474 return [0, 0]; 3475 } 3476 }, 3477 3478 /** 3479 * Used by old versions of CustomEvent, restored for backwards 3480 * compatibility 3481 * @method regCE 3482 * @private 3483 * @static 3484 * @deprecated still here for backwards compatibility 3485 */ 3486 regCE: function() {}, 3487 3488 /** 3489 * Adds a DOM event directly without the caching, cleanup, context adj, etc 3490 * 3491 * @method _simpleAdd 3492 * @param {HTMLElement} el the element to bind the handler to 3493 * @param {string} sType the type of event handler 3494 * @param {function} fn the callback to invoke 3495 * @param {boolen} capture capture or bubble phase 3496 * @static 3497 * @private 3498 */ 3499 _simpleAdd: function () { 3500 if (window.addEventListener) { 3501 return function(el, sType, fn, capture) { 3502 el.addEventListener(sType, fn, (capture)); 3503 }; 3504 } else if (window.attachEvent) { 3505 return function(el, sType, fn, capture) { 3506 el.attachEvent("on" + sType, fn); 3507 }; 3508 } else { 3509 return function(){}; 3510 } 3511 }(), 3512 3513 /** 3514 * Basic remove listener 3515 * 3516 * @method _simpleRemove 3517 * @param {HTMLElement} el the element to bind the handler to 3518 * @param {string} sType the type of event handler 3519 * @param {function} fn the callback to invoke 3520 * @param {boolen} capture capture or bubble phase 3521 * @static 3522 * @private 3523 */ 3524 _simpleRemove: function() { 3525 if (window.removeEventListener) { 3526 return function (el, sType, fn, capture) { 3527 el.removeEventListener(sType, fn, (capture)); 3528 }; 3529 } else if (window.detachEvent) { 3530 return function (el, sType, fn) { 3531 el.detachEvent("on" + sType, fn); 3532 }; 3533 } else { 3534 return function(){}; 3535 } 3536 }() 3537 }; 3538 3539 }(); 3540 3541 (function() { 3542 var EU = YAHOO.util.Event; 3543 3544 /** 3545 * Appends an event handler. This is an alias for <code>addListener</code> 3546 * 3547 * @method on 3548 * 3549 * @param {String|HTMLElement|Array|NodeList} el An id, an element 3550 * reference, or a collection of ids and/or elements to assign the 3551 * listener to. 3552 * @param {String} sType The type of event to append 3553 * @param {Function} fn The method the event invokes 3554 * @param {Object} obj An arbitrary object that will be 3555 * passed as a parameter to the handler 3556 * @param {Boolean|object} overrideContext If true, the obj passed in becomes 3557 * the execution context of the listener. If an 3558 * object, this object becomes the execution 3559 * context. 3560 * @return {Boolean} True if the action was successful or defered, 3561 * false if one or more of the elements 3562 * could not have the listener attached, 3563 * or if the operation throws an exception. 3564 * @static 3565 */ 3566 EU.on = EU.addListener; 3567 3568 /** 3569 * YAHOO.util.Event.onFocus is an alias for addFocusListener 3570 * @method onFocus 3571 * @see addFocusListener 3572 * @static 3573 * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type. 3574 */ 3575 EU.onFocus = EU.addFocusListener; 3576 3577 /** 3578 * YAHOO.util.Event.onBlur is an alias for addBlurListener 3579 * @method onBlur 3580 * @see addBlurListener 3581 * @static 3582 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type. 3583 */ 3584 EU.onBlur = EU.addBlurListener; 3585 3586/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ 3587 3588 // Internet Explorer: use the readyState of a defered script. 3589 // This isolates what appears to be a safe moment to manipulate 3590 // the DOM prior to when the document's readyState suggests 3591 // it is safe to do so. 3592 if (EU.isIE && EU.isIE < 11) { 3593 if (self !== self.top) { 3594 document.onreadystatechange = function() { 3595 if (document.readyState == 'complete') { 3596 document.onreadystatechange = null; 3597 EU._ready(); 3598 } 3599 }; 3600 } else { 3601 3602 // Process onAvailable/onContentReady items when the 3603 // DOM is ready. 3604 YAHOO.util.Event.onDOMReady( 3605 YAHOO.util.Event._tryPreloadAttach, 3606 YAHOO.util.Event, true); 3607 3608 var n = document.createElement('p'); 3609 3610 EU._dri = setInterval(function() { 3611 try { 3612 // throws an error if doc is not ready 3613 n.doScroll('left'); 3614 clearInterval(EU._dri); 3615 EU._dri = null; 3616 EU._ready(); 3617 n = null; 3618 } catch (ex) { 3619 } 3620 }, EU.POLL_INTERVAL); 3621 } 3622 3623 // The document's readyState in Safari currently will 3624 // change to loaded/complete before images are loaded. 3625 } else if (EU.webkit && EU.webkit < 525) { 3626 3627 EU._dri = setInterval(function() { 3628 var rs=document.readyState; 3629 if ("loaded" == rs || "complete" == rs) { 3630 clearInterval(EU._dri); 3631 EU._dri = null; 3632 EU._ready(); 3633 } 3634 }, EU.POLL_INTERVAL); 3635 3636 // FireFox and Opera: These browsers provide a event for this 3637 // moment. The latest WebKit releases now support this event. 3638 } else { 3639 3640 EU._simpleAdd(document, "DOMContentLoaded", EU._ready); 3641 3642 }
3643 ///////////////////////////////////////////////////////////// 3644 3645 3646 EU._simpleAdd(window, "load", EU._load); 3647 EU._simpleAdd(window, "unload", EU._unload); 3648 EU._tryPreloadAttach(); 3649 })(); 3650 3651} 3652/** 3653 * EventProvider is designed to be used with YAHOO.augment to wrap 3654 * CustomEvents in an interface that allows events to be subscribed to 3655 * and fired by name. This makes it possible for implementing code to 3656 * subscribe to an event that either has not been created yet, or will 3657 * not be created at all. 3658 * 3659 * @Class EventProvider 3660 */ 3661YAHOO.util.EventProvider = function() { }; 3662 3663YAHOO.util.EventProvider.prototype = { 3664 3665 /** 3666 * Private storage of custom events 3667 * @property __yui_events 3668 * @type Object[] 3669 * @private 3670 */ 3671 __yui_events: null, 3672 3673 /** 3674 * Private storage of custom event subscribers 3675 * @property __yui_subscribers 3676 * @type Object[] 3677 * @private 3678 */ 3679 __yui_subscribers: null, 3680 3681 /** 3682 * Subscribe to a CustomEvent by event type 3683 * 3684 * @method subscribe 3685 * @param p_type {string} the type, or name of the event 3686 * @param p_fn {function} the function to exectute when the event fires 3687 * @param p_obj {Object} An object to be passed along when the event 3688 * fires 3689 * @param overrideContext {boolean} If true, the obj passed in becomes the 3690 * execution scope of the listener 3691 */ 3692 subscribe: function(p_type, p_fn, p_obj, overrideContext) { 3693 3694 this.__yui_events = this.__yui_events || {}; 3695 var ce = this.__yui_events[p_type]; 3696 3697 if (ce) { 3698 ce.subscribe(p_fn, p_obj, overrideContext); 3699 } else { 3700 this.__yui_subscribers = this.__yui_subscribers || {}; 3701 var subs = this.__yui_subscribers; 3702 if (!subs[p_type]) { 3703 subs[p_type] = []; 3704 } 3705 subs[p_type].push( 3706 { fn: p_fn, obj: p_obj, overrideContext: overrideContext } ); 3707 } 3708 }, 3709 3710 /** 3711 * Unsubscribes one or more listeners the from the specified event 3712 * @method unsubscribe 3713 * @param p_type {string} The type, or name of the event. If the type 3714 * is not specified, it will attempt to remove 3715 * the listener from all hosted events. 3716 * @param p_fn {Function} The subscribed function to unsubscribe, if not 3717 * supplied, all subscribers will be removed. 3718 * @param p_obj {Object} The custom object passed to subscribe. This is 3719 * optional, but if supplied will be used to 3720 * disambiguate multiple listeners that are the same 3721 * (e.g., you subscribe many object using a function 3722 * that lives on the prototype) 3723 * @return {boolean} true if the subscriber was found and detached. 3724 */ 3725 unsubscribe: function(p_type, p_fn, p_obj) { 3726 this.__yui_events = this.__yui_events || {}; 3727 var evts = this.__yui_events; 3728 if (p_type) { 3729 var ce = evts[p_type]; 3730 if (ce) { 3731 return ce.unsubscribe(p_fn, p_obj); 3732 } 3733 } else { 3734 var ret = true; 3735 for (var i in evts) { 3736 if (YAHOO.lang.hasOwnProperty(evts, i)) { 3737 ret = ret && evts[i].unsubscribe(p_fn, p_obj); 3738 } 3739 } 3740 return ret; 3741 } 3742 3743 return false; 3744 }, 3745 3746 /** 3747 * Removes all listeners from the specified event. If the event type 3748 * is not specified, all listeners from all hosted custom events will 3749 * be removed. 3750 * @method unsubscribeAll 3751 * @param p_type {string} The type, or name of the event 3752 */ 3753 unsubscribeAll: function(p_type) { 3754 return this.unsubscribe(p_type); 3755 }, 3756 3757 /** 3758 * Creates a new custom event of the specified type. If a custom event 3759 * by that name already exists, it will not be re-created. In either 3760 * case the custom event is returned. 3761 * 3762 * @method createEvent 3763 * 3764 * @param p_type {string} the type, or name of the event 3765 * @param p_config {object} optional config params. Valid properties are: 3766 * 3767 * <ul> 3768 * <li> 3769 * scope: defines the default execution scope. If not defined 3770 * the default scope will be this instance. 3771 * </li> 3772 * <li> 3773 * silent: if true, the custom event will not generate log messages. 3774 * This is false by default. 3775 * </li> 3776 * <li>
3777 * fireOnce: if true, the custom event will only notify subscribers 3778 * once regardless of the number of times the event is fired. In 3779 * addition, new subscribers will be executed immediately if the 3780 * event has already fired. 3781 * This is false by default. 3782 * </li> 3783 * <li> 3784 * onSubscribeCallback: specifies a callback to execute when the 3785 * event has a new subscriber. This will fire immediately for 3786 * each queued subscriber if any exist prior to the creation of 3787 * the event. 3788 * </li> 3789 * </ul> 3790 * 3791 * @return {CustomEvent} the custom event 3792 * 3793 */ 3794 createEvent: function(p_type, p_config) { 3795 3796 this.__yui_events = this.__yui_events || {}; 3797 var opts = p_config || {}, 3798 events = this.__yui_events, ce; 3799 3800 if (events[p_type]) { 3801YAHOO.log("EventProvider createEvent skipped: '"+p_type+"' already exists"); 3802 } else { 3803 3804 ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent, 3805 YAHOO.util.CustomEvent.FLAT, opts.fireOnce); 3806 3807 events[p_type] = ce; 3808 3809 if (opts.onSubscribeCallback) { 3810 ce.subscribeEvent.subscribe(opts.onSubscribeCallback); 3811 } 3812 3813 this.__yui_subscribers = this.__yui_subscribers || {}; 3814 var qs = this.__yui_subscribers[p_type]; 3815 3816 if (qs) { 3817 for (var i=0; i<qs.length; ++i) { 3818 ce.subscribe(qs[i].fn, qs[i].obj, qs[i].overrideContext); 3819 } 3820 } 3821 } 3822 3823 return events[p_type]; 3824 }, 3825 3826 3827 /** 3828 * Fire a custom event by name. The callback functions will be executed 3829 * from the scope specified when the event was created, and with the 3830 * following parameters: 3831 * <ul> 3832 * <li>The first argument fire() was executed with</li> 3833 * <li>The custom object (if any) that was passed into the subscribe() 3834 * method</li> 3835 * </ul> 3836 * @method fireEvent 3837 * @param p_type {string} the type, or name of the event 3838 * @param arguments {Object*} an arbitrary set of parameters to pass to 3839 * the handler. 3840 * @return {boolean} the return value from CustomEvent.fire 3841 * 3842 */ 3843 fireEvent: function(p_type) { 3844 3845 this.__yui_events = this.__yui_events || {}; 3846 var ce = this.__yui_events[p_type]; 3847 3848 if (!ce) { 3849YAHOO.log(p_type + "event fired before it was created."); 3850 return null; 3851 } 3852 3853 var args = []; 3854 for (var i=1; i<arguments.length; ++i) { 3855 args.push(arguments[i]); 3856 } 3857 return ce.fire.apply(ce, args); 3858 }, 3859 3860 /** 3861 * Returns true if the custom event of the provided type has been created 3862 * with createEvent. 3863 * @method hasEvent 3864 * @param type {string} the type, or name of the event 3865 */ 3866 hasEvent: function(type) { 3867 if (this.__yui_events) { 3868 if (this.__yui_events[type]) { 3869 return true; 3870 } 3871 } 3872 return false; 3873 } 3874 3875}; 3876 3877(function() { 3878 3879 var Event = YAHOO.util.Event, Lang = YAHOO.lang; 3880 3881/** 3882* KeyListener is a utility that provides an easy interface for listening for 3883* keydown/keyup events fired against DOM elements. 3884* @namespace YAHOO.util 3885* @class KeyListener 3886* @constructor 3887* @param {HTMLElement} attachTo The element or element ID to which the key 3888* event should be attached 3889* @param {String} attachTo The element or element ID to which the key 3890* event should be attached 3891* @param {Object} keyData The object literal representing the key(s) 3892* to detect. Possible attributes are 3893* shift(boolean), alt(boolean), ctrl(boolean) 3894* and keys(either an int or an array of ints 3895* representing keycodes). 3896* @param {Function} handler The CustomEvent handler to fire when the 3897* key event is detected 3898* @param {Object} handler An object literal representing the handler. 3899* @param {String} event Optional. The event (keydown or keyup) to 3900* listen for. Defaults automatically to keydown. 3901* 3902* @knownissue the "keypress" event is completely broken in Safari 2.x and below. 3903* the workaround is use "keydown" for key listening. However, if
3904* it is desired to prevent the default behavior of the keystroke, 3905* that can only be done on the keypress event. This makes key 3906* handling quite ugly. 3907* @knownissue keydown is also broken in Safari 2.x and below for the ESC key. 3908* There currently is no workaround other than choosing another 3909* key to listen for. 3910*/ 3911YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) { 3912 if (!attachTo) { 3913 YAHOO.log("No attachTo element specified", "error"); 3914 } else if (!keyData) { 3915 YAHOO.log("No keyData specified", "error"); 3916 } else if (!handler) { 3917 YAHOO.log("No handler specified", "error"); 3918 } 3919 3920 if (!event) { 3921 event = YAHOO.util.KeyListener.KEYDOWN; 3922 } 3923 3924 /** 3925 * The CustomEvent fired internally when a key is pressed 3926 * @event keyEvent 3927 * @private 3928 * @param {Object} keyData The object literal representing the key(s) to 3929 * detect. Possible attributes are shift(boolean), 3930 * alt(boolean), ctrl(boolean) and keys(either an 3931 * int or an array of ints representing keycodes). 3932 */ 3933 var keyEvent = new YAHOO.util.CustomEvent("keyPressed"); 3934 3935 /** 3936 * The CustomEvent fired when the KeyListener is enabled via the enable() 3937 * function 3938 * @event enabledEvent 3939 * @param {Object} keyData The object literal representing the key(s) to 3940 * detect. Possible attributes are shift(boolean), 3941 * alt(boolean), ctrl(boolean) and keys(either an 3942 * int or an array of ints representing keycodes). 3943 */ 3944 this.enabledEvent = new YAHOO.util.CustomEvent("enabled"); 3945 3946 /** 3947 * The CustomEvent fired when the KeyListener is disabled via the 3948 * disable() function 3949 * @event disabledEvent 3950 * @param {Object} keyData The object literal representing the key(s) to 3951 * detect. Possible attributes are shift(boolean), 3952 * alt(boolean), ctrl(boolean) and keys(either an 3953 * int or an array of ints representing keycodes). 3954 */ 3955 this.disabledEvent = new YAHOO.util.CustomEvent("disabled"); 3956 3957 if (Lang.isString(attachTo)) { 3958 attachTo = document.getElementById(attachTo); // No Dom util 3959 } 3960 3961 if (Lang.isFunction(handler)) { 3962 keyEvent.subscribe(handler); 3963 } else { 3964 keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope); 3965 } 3966 3967 /** 3968 * Handles the key event when a key is pressed. 3969 * @method handleKeyPress 3970 * @param {DOMEvent} e The keypress DOM event 3971 * @param {Object} obj The DOM event scope object 3972 * @private 3973 */ 3974 function handleKeyPress(e, obj) { 3975 if (! keyData.shift) { 3976 keyData.shift = false; 3977 } 3978 if (! keyData.alt) { 3979 keyData.alt = false; 3980 } 3981 if (! keyData.ctrl) { 3982 keyData.ctrl = false; 3983 } 3984 3985 // check held down modifying keys first 3986 if (e.shiftKey == keyData.shift && 3987 e.altKey == keyData.alt && 3988 e.ctrlKey == keyData.ctrl) { // if we pass this, all modifiers match 3989 3990 var dataItem, keys = keyData.keys, key; 3991 3992 if (YAHOO.lang.isArray(keys)) { 3993 for (var i=0;i<keys.length;i++) { 3994 dataItem = keys[i]; 3995 key = Event.getCharCode(e); 3996 3997 if (dataItem == key) { 3998 keyEvent.fire(key, e); 3999 break; 4000 } 4001 } 4002 } else { 4003 key = Event.getCharCode(e); 4004 if (keys == key ) { 4005 keyEvent.fire(key, e); 4006 } 4007 } 4008 } 4009 } 4010 4011 /** 4012 * Enables the KeyListener by attaching the DOM event listeners to the 4013 * target DOM element 4014 * @method enable 4015 */ 4016 this.enable = function() { 4017 if (! this.enabled) { 4018 Event.on(attachTo, event, handleKeyPress);
4019 this.enabledEvent.fire(keyData); 4020 } 4021 /** 4022 * Boolean indicating the enabled/disabled state of the Tooltip 4023 * @property enabled 4024 * @type Boolean 4025 */ 4026 this.enabled = true; 4027 }; 4028 4029 /** 4030 * Disables the KeyListener by removing the DOM event listeners from the 4031 * target DOM element 4032 * @method disable 4033 */ 4034 this.disable = function() { 4035 if (this.enabled) { 4036 Event.removeListener(attachTo, event, handleKeyPress); 4037 this.disabledEvent.fire(keyData); 4038 } 4039 this.enabled = false; 4040 }; 4041 4042 /** 4043 * Returns a String representation of the object. 4044 * @method toString 4045 * @return {String} The string representation of the KeyListener 4046 */ 4047 this.toString = function() { 4048 return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 4049 (attachTo.id ? "[" + attachTo.id + "]" : ""); 4050 }; 4051 4052}; 4053 4054var KeyListener = YAHOO.util.KeyListener; 4055 4056/** 4057 * Constant representing the DOM "keydown" event. 4058 * @property YAHOO.util.KeyListener.KEYDOWN 4059 * @static 4060 * @final 4061 * @type String 4062 */ 4063KeyListener.KEYDOWN = "keydown"; 4064 4065/** 4066 * Constant representing the DOM "keyup" event. 4067 * @property YAHOO.util.KeyListener.KEYUP 4068 * @static 4069 * @final 4070 * @type String 4071 */ 4072KeyListener.KEYUP = "keyup"; 4073 4074/** 4075 * keycode constants for a subset of the special keys 4076 * @property KEY 4077 * @static 4078 * @final 4079 */ 4080KeyListener.KEY = { 4081 ALT : 18, 4082 BACK_SPACE : 8, 4083 CAPS_LOCK : 20, 4084 CONTROL : 17, 4085 DELETE : 46, 4086 DOWN : 40, 4087 END : 35, 4088 ENTER : 13, 4089 ESCAPE : 27, 4090 HOME : 36, 4091 LEFT : 37, 4092 META : 224, 4093 NUM_LOCK : 144, 4094 PAGE_DOWN : 34, 4095 PAGE_UP : 33, 4096 PAUSE : 19, 4097 PRINTSCREEN : 44, 4098 RIGHT : 39, 4099 SCROLL_LOCK : 145, 4100 SHIFT : 16, 4101 SPACE : 32, 4102 TAB : 9, 4103 UP : 38 4104}; 4105 4106})(); 4107YAHOO.register("event", YAHOO.util.Event, {version: "2.9.0", build: "2800"}); 4108/* 4109Copyright (c) 2011, Yahoo! Inc. All rights reserved. 4110Code licensed under the BSD License: 4111http://developer.yahoo.com/yui/license.html 4112version: 2.9.0 4113*/ 4114/** 4115 * The dom module provides helper methods for manipulating Dom elements. 4116 * @module dom 4117 * 4118 */ 4119 4120(function() { 4121 // for use with generateId (global to save state if Dom is overwritten) 4122 YAHOO.env._id_counter = YAHOO.env._id_counter || 0; 4123 4124 // internal shorthand 4125 var Y = YAHOO.util, 4126 lang = YAHOO.lang, 4127 UA = YAHOO.env.ua, 4128 trim = YAHOO.lang.trim, 4129 propertyCache = {}, // for faster hyphen converts 4130 reCache = {}, // cache className regexes 4131 RE_TABLE = /^t(?:able|d|h)$/i, // for _calcBorders 4132 RE_COLOR = /color$/i, 4133 4134 // DOM aliases 4135 document = window.document, 4136 documentElement = document.documentElement, 4137 4138 // string constants 4139 OWNER_DOCUMENT = 'ownerDocument', 4140 DEFAULT_VIEW = 'defaultView', 4141 DOCUMENT_ELEMENT = 'documentElement', 4142 COMPAT_MODE = 'compatMode', 4143 OFFSET_LEFT = 'offsetLeft', 4144 OFFSET_TOP = 'offsetTop', 4145 OFFSET_PARENT = 'offsetParent', 4146 PARENT_NODE = 'parentNode', 4147 NODE_TYPE = 'nodeType', 4148 TAG_NAME = 'tagName', 4149 SCROLL_LEFT = 'scrollLeft', 4150 SCROLL_TOP = 'scrollTop', 4151 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', 4152 GET_COMPUTED_STYLE = 'getComputedStyle', 4153 CURRENT_STYLE = 'currentStyle', 4154 CSS1_COMPAT = 'CSS1Compat', 4155 _BACK_COMPAT = 'BackCompat', 4156 _CLASS = 'class', // underscore due to reserved word 4157 CLASS_NAME = 'className', 4158 EMPTY = '', 4159 SPACE = ' ', 4160 C_START = '(?:^|\\s)', 4161 C_END = '(?= |$)', 4162 G = 'g', 4163 POSITION = 'position', 4164 FIXED = 'fixed', 4165 RELATIVE = 'relative', 4166 LEFT = 'left', 4167 TOP = 'top', 4168 MEDIUM = 'medium', 4169 BORDER_LEFT_WIDTH = 'borderLeftWidth', 4170 BORDER_TOP_WIDTH = 'borderTopWidth', 4171 4172 // brower detection 4173 isOpera = UA.opera, 4174 isSafari = UA.webkit, 4175 isGecko = UA.gecko, 4176 isIE = UA.ie; 4177 4178 /** 4179 * Provides helper methods for DOM elements. 4180 * @namespace YAHOO.util 4181 * @class Dom 4182 * @requires yahoo, event 4183 */ 4184 Y.Dom = { 4185 CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8 4186 'for': 'htmlFor', 4187 'class': CLASS_NAME 4188 } : { // w3c 4189 'htmlFor': 'for', 4190 'className': _CLASS 4191 }, 4192 4193 DOT_ATTRIBUTES: { 4194 checked: true 4195 }, 4196 4197 /** 4198 * Returns an HTMLElement reference. 4199 * @method get 4200 * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/
4200or HTMLElements. 4201 * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements. 4202 */ 4203 get: function(el) { 4204 var id, nodes, c, i, len, attr, ret = null; 4205 4206 if (el) { 4207 if (typeof el == 'string' || typeof el == 'number') { // id 4208 id = el + ''; 4209 el = document.getElementById(el); 4210 attr = (el) ? el.attributes : null; 4211 if (el && attr && attr.id && attr.id.value === id) { // IE: avoid false match on "name" attribute 4212 return el; 4213 } else if (el && document.all) { // filter by name 4214 el = null; 4215 nodes = document.all[id]; 4216 if (nodes && nodes.length) { 4217 for (i = 0, len = nodes.length; i < len; ++i) { 4218 if (nodes[i].id === id) { 4219 return nodes[i]; 4220 } 4221 } 4222 } 4223 } 4224 } else if (Y.Element && el instanceof Y.Element) { 4225 el = el.get('element'); 4226 } else if (!el.nodeType && 'length' in el) { // array-like 4227 c = []; 4228 for (i = 0, len = el.length; i < len; ++i) { 4229 c[c.length] = Y.Dom.get(el[i]); 4230 } 4231 4232 el = c; 4233 } 4234 4235 ret = el; 4236 } 4237 4238 return ret; 4239 }, 4240 4241 getComputedStyle: function(el, property) { 4242 if (window[GET_COMPUTED_STYLE]) { 4243 return el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null)[property]; 4244 } else if (el[CURRENT_STYLE]) { 4245 return Y.Dom.IE_ComputedStyle.get(el, property); 4246 } 4247 }, 4248 4249 /** 4250 * Normalizes currentStyle and ComputedStyle. 4251 * @method getStyle 4252 * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. 4253 * @param {String} property The style property whose value is returned. 4254 * @return {String | Array} The current value of the style property for the element(s). 4255 */ 4256 getStyle: function(el, property) { 4257 return Y.Dom.batch(el, Y.Dom._getStyle, property); 4258 }, 4259 4260 // branching at load instead of runtime 4261 _getStyle: function() { 4262 if (window[GET_COMPUTED_STYLE]) { // W3C DOM method 4263 return function(el, property) { 4264 property = (property === 'float') ? property = 'cssFloat' : 4265 Y.Dom._toCamel(property); 4266 4267 var value = el.style[property], 4268 computed; 4269 4270 if (!value) { 4271 computed = el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null); 4272 if (computed) { // test computed before touching for safari 4273 value = computed[property]; 4274 } 4275 } 4276 4277 return value; 4278 }; 4279 } else if (documentElement[CURRENT_STYLE]) { 4280 return function(el, property) { 4281 var value; 4282 4283 switch(property) { 4284 case 'opacity' :// IE opacity uses filter 4285 value = 100; 4286 try { // will error if no DXImageTransform 4287 value = el.filters['DXImageTransform.Microsoft.Alpha'].opacity; 4288 4289 } catch(e) { 4290 try { // make sure its in the document 4291 value = el.filters('alpha').opacity; 4292 } catch(err) { 4293 YAHOO.log('getStyle: IE filter failed', 4294 'error', 'Dom'); 4295 } 4296 } 4297 return value / 100; 4298 case 'float': // fix reserved word 4299 property = 'styleFloat'; // fall through 4300 default: 4301 property = Y.Dom._toCamel(property); 4302 value = el[CURRENT_STYLE] ? el[CURRENT_STYLE][property] : null; 4303 return ( el.style[property] || value ); 4304 } 4305 }; 4306 } 4307 }(), 4308 4309 /** 4310 * Wrapper for setting style properties of HTMLElements. Normalizes "opacity" across modern browsers. 4311 * @method setStyle 4312 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/
4312or HTMLElements. 4313 * @param {String} property The style property to be set. 4314 * @param {String} val The value to apply to the given property. 4315 */ 4316 setStyle: function(el, property, val) { 4317 Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val }); 4318 }, 4319 4320 _setStyle: function() { 4321 if (!window.getComputedStyle && document.documentElement.currentStyle) { 4322 return function(el, args) { 4323 var property = Y.Dom._toCamel(args.prop), 4324 val = args.val; 4325 4326 if (el) { 4327 switch (property) { 4328 case 'opacity': 4329 // remove filter if unsetting or full opacity 4330 if (val === '' || val === null || val === 1) { 4331 el.style.removeAttribute('filter'); 4332 } else if ( lang.isString(el.style.filter) ) { // in case not appended 4333 el.style.filter = 'alpha(opacity=' + val * 100 + ')'; 4334 4335 if (!el[CURRENT_STYLE] || !el[CURRENT_STYLE].hasLayout) { 4336 el.style.zoom = 1; // when no layout or cant tell 4337 } 4338 } 4339 break; 4340 case 'float': 4341 property = 'styleFloat'; 4342 default: 4343 el.style[property] = val; 4344 } 4345 } else { 4346 YAHOO.log('element ' + el + ' is undefined', 'error', 'Dom'); 4347 } 4348 }; 4349 } else { 4350 return function(el, args) { 4351 var property = Y.Dom._toCamel(args.prop), 4352 val = args.val; 4353 if (el) { 4354 if (property == 'float') { 4355 property = 'cssFloat'; 4356 } 4357 el.style[property] = val; 4358 } else { 4359 YAHOO.log('element ' + el + ' is undefined', 'error', 'Dom'); 4360 } 4361 }; 4362 } 4363 4364 }(), 4365 4366 /** 4367 * Gets the current position of an element based on page coordinates. 4368 * Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4369 * @method getXY 4370 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM 4371 * reference, or an Array of IDs and/or HTMLElements 4372 * @return {Array} The XY position of the element(s) 4373 */ 4374 getXY: function(el) { 4375 return Y.Dom.batch(el, Y.Dom._getXY); 4376 }, 4377 4378 _canPosition: function(el) { 4379 return ( Y.Dom._getStyle(el, 'display') !== 'none' && Y.Dom._inDoc(el) ); 4380 }, 4381 4382 _getXY: function(node) { 4383 var scrollLeft, scrollTop, box, doc, 4384 clientTop, clientLeft, 4385 round = Math.round, // TODO: round? 4386 xy = false; 4387 4388 if (Y.Dom._canPosition(node)) { 4389 box = node[GET_BOUNDING_CLIENT_RECT](); 4390 doc = node[OWNER_DOCUMENT]; 4391 scrollLeft = Y.Dom.getDocumentScrollLeft(doc); 4392 scrollTop = Y.Dom.getDocumentScrollTop(doc); 4393 xy = [box[LEFT], box[TOP]]; 4394 4395 // remove IE default documentElement offset (border) 4396 if (clientTop || clientLeft) { 4397 xy[0] -= clientLeft; 4398 xy[1] -= clientTop; 4399 } 4400 4401 if ((scrollTop || scrollLeft)) { 4402 xy[0] += scrollLeft; 4403 xy[1] += scrollTop; 4404 } 4405 4406 // gecko may return sub-pixel (non-int) values 4407 xy[0] = round(xy[0]); 4408 xy[1] = round(xy[1]); 4409 } else { 4410 YAHOO.log('getXY failed: element not positionable (either not in a document or not displayed)', 'error', 'Dom'); 4411 } 4412 4413 return xy; 4414 }, 4415 4416 /** 4417 * Gets the current X position of an element based on page coordinates. The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4418 * @method getX 4419 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/
4419or HTMLElements 4420 * @return {Number | Array} The X position of the element(s) 4421 */ 4422 getX: function(el) { 4423 var f = function(el) { 4424 return Y.Dom.getXY(el)[0]; 4425 }; 4426 4427 return Y.Dom.batch(el, f, Y.Dom, true); 4428 }, 4429 4430 /** 4431 * Gets the current Y position of an element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4432 * @method getY 4433 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements 4434 * @return {Number | Array} The Y position of the element(s) 4435 */ 4436 getY: function(el) { 4437 var f = function(el) { 4438 return Y.Dom.getXY(el)[1]; 4439 }; 4440 4441 return Y.Dom.batch(el, f, Y.Dom, true); 4442 }, 4443 4444 /** 4445 * Set the position of an html element in page coordinates, regardless of how the element is positioned. 4446 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4447 * @method setXY 4448 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements 4449 * @param {Array} pos Contains X & Y values for new position (coordinates are page-based) 4450 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails 4451 */ 4452 setXY: function(el, pos, noRetry) { 4453 Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry }); 4454 }, 4455 4456 _setXY: function(node, args) { 4457 var pos = Y.Dom._getStyle(node, POSITION), 4458 setStyle = Y.Dom.setStyle, 4459 xy = args.pos, 4460 noRetry = args.noRetry, 4461 4462 delta = [ // assuming pixels; if not we will have to retry 4463 parseInt( Y.Dom.getComputedStyle(node, LEFT), 10 ), 4464 parseInt( Y.Dom.getComputedStyle(node, TOP), 10 ) 4465 ], 4466 4467 currentXY, 4468 newXY; 4469 4470 currentXY = Y.Dom._getXY(node); 4471 4472 if (!xy || currentXY === false) { // has to be part of doc to have xy 4473 YAHOO.log('xy failed: node not available', 'error', 'Node'); 4474 return false; 4475 } 4476 4477 if (pos == 'static') { // default to relative 4478 pos = RELATIVE; 4479 setStyle(node, POSITION, pos); 4480 } 4481 4482 if ( isNaN(delta[0]) ) {// in case of 'auto' 4483 delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT]; 4484 } 4485 if ( isNaN(delta[1]) ) { // in case of 'auto' 4486 delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP]; 4487 } 4488 4489 if (xy[0] !== null) { // from setX 4490 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px'); 4491 } 4492 4493 if (xy[1] !== null) { // from setY 4494 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px'); 4495 } 4496 4497 if (!noRetry) { 4498 newXY = Y.Dom._getXY(node); 4499 4500 // if retry is true, try one more time if we miss 4501 if ( (xy[0] !== null && newXY[0] != xy[0]) || 4502 (xy[1] !== null && newXY[1] != xy[1]) ) { 4503 Y.Dom._setXY(node, { pos: xy, noRetry: true }); 4504 } 4505 } 4506 4507 YAHOO.log('setXY setting position to ' + xy, 'info', 'Node'); 4508 }, 4509 4510 /** 4511 * Set the X position of an html element in page coordinates, regardless of how the element is positioned. 4512 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4513 * @method setX 4514 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/
4514or HTMLElements. 4515 * @param {Int} x The value to use as the X coordinate for the element(s). 4516 */ 4517 setX: function(el, x) { 4518 Y.Dom.setXY(el, [x, null]); 4519 }, 4520 4521 /** 4522 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned. 4523 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). 4524 * @method setY 4525 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. 4526 * @param {Int} x To use as the Y coordinate for the element(s). 4527 */ 4528 setY: function(el, y) { 4529 Y.Dom.setXY(el, [null, y]); 4530 }, 4531 4532 /** 4533 * Returns the region position of the given element. 4534 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false). 4535 * @method getRegion 4536 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. 4537 * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data. 4538 */ 4539 getRegion: function(el) { 4540 var f = function(el) { 4541 var region = false; 4542 if ( Y.Dom._canPosition(el) ) { 4543 region = Y.Region.getRegion(el); 4544 YAHOO.log('getRegion returning ' + region, 'info', 'Dom'); 4545 } else { 4546 YAHOO.log('getRegion failed: element not positionable (either not in a document or not displayed)', 'error', 'Dom'); 4547 } 4548 4549 return region; 4550 }; 4551 4552 return Y.Dom.batch(el, f, Y.Dom, true); 4553 }, 4554 4555 /** 4556 * Returns the width of the client (viewport). 4557 * @method getClientWidth 4558 * @deprecated Now using getViewportWidth. This interface left intact for back compat. 4559 * @return {Int} The width of the viewable area of the page. 4560 */ 4561 getClientWidth: function() { 4562 return Y.Dom.getViewportWidth(); 4563 }, 4564 4565 /** 4566 * Returns the height of the client (viewport). 4567 * @method getClientHeight 4568 * @deprecated Now using getViewportHeight. This interface left intact for back compat. 4569 * @return {Int} The height of the viewable area of the page. 4570 */ 4571 getClientHeight: function() { 4572 return Y.Dom.getViewportHeight(); 4573 }, 4574 4575 /** 4576 * Returns an array of HTMLElements with the given class. 4577 * For optimized performance, include a tag and/or root node when possible. 4578 * Note: This method operates against a live collection, so modifying the 4579 * collection in the callback (removing/appending nodes, etc.) will have 4580 * side effects. Instead you should iterate the returned nodes array, 4581 * as you would with the native "getElementsByTagName" method. 4582 * @method getElementsByClassName 4583 * @param {String} className The class name to match against 4584 * @param {String} tag (optional) The tag name of the elements being collected 4585 * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point. 4586 * This element is not included in the className scan. 4587 * @param {Function} apply (optional) A function to apply to each element when found 4588 * @param {Any} o (optional) An optional arg that is passed to the supplied method 4589 * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" 4590 * @return {Array} An array of elements that have the given class name 4591 */ 4592 getElementsByClassName: function(className, tag, root, apply, o, overrides) { 4593 tag = tag || '*'; 4594 root = (root) ? Y.Dom.get(root) : null || document; 4595 if (!root) { 4596 return []; 4597 } 4598 4599 var nodes = [], 4600 elements = root.getElementsByTagName(tag), 4601 hasClass = Y.Dom.hasClass; 4602 4603 for (var i = 0, len = elements.length; i < len; ++i) { 4604 if ( hasClass(elements[i], className) ) { 4605 nodes[nodes.length] = elements[i]; 4606 } 4607 } 4608
4609 if (apply) { 4610 Y.Dom.batch(nodes, apply, o, overrides); 4611 } 4612 4613 return nodes; 4614 }, 4615 4616 /** 4617 * Determines whether an HTMLElement has the given className. 4618 * @method hasClass 4619 * @param {String | HTMLElement | Array} el The element or collection to test 4620 * @param {String | RegExp} className the class name to search for, or a regular 4621 * expression to match against 4622 * @return {Boolean | Array} A boolean value or array of boolean values 4623 */ 4624 hasClass: function(el, className) { 4625 return Y.Dom.batch(el, Y.Dom._hasClass, className); 4626 }, 4627 4628 _hasClass: function(el, className) { 4629 var ret = false, 4630 current; 4631 4632 if (el && className) { 4633 current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; 4634 if (current) { // convert line breaks, tabs and other delims to spaces 4635 current = current.replace(/\s+/g, SPACE); 4636 } 4637 4638 if (className.exec) { 4639 ret = className.test(current); 4640 } else { 4641 ret = className && (SPACE + current + SPACE). 4642 indexOf(SPACE + className + SPACE) > -1; 4643 } 4644 } else { 4645 YAHOO.log('hasClass called with invalid arguments', 'warn', 'Dom'); 4646 } 4647 4648 return ret; 4649 }, 4650 4651 /** 4652 * Adds a class name to a given element or collection of elements. 4653 * @method addClass 4654 * @param {String | HTMLElement | Array} el The element or collection to add the class to 4655 * @param {String} className the class name to add to the class attribute 4656 * @return {Boolean | Array} A pass/fail boolean or array of booleans 4657 */ 4658 addClass: function(el, className) { 4659 return Y.Dom.batch(el, Y.Dom._addClass, className); 4660 }, 4661 4662 _addClass: function(el, className) { 4663 var ret = false, 4664 current; 4665 4666 if (el && className) { 4667 current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; 4668 if ( !Y.Dom._hasClass(el, className) ) { 4669 Y.Dom.setAttribute(el, CLASS_NAME, trim(current + SPACE + className)); 4670 ret = true; 4671 } 4672 } else { 4673 YAHOO.log('addClass called with invalid arguments', 'warn', 'Dom'); 4674 } 4675 4676 return ret; 4677 }, 4678 4679 /** 4680 * Removes a class name from a given element or collection of elements. 4681 * @method removeClass 4682 * @param {String | HTMLElement | Array} el The element or collection to remove the class from 4683 * @param {String} className the class name to remove from the class attribute 4684 * @return {Boolean | Array} A pass/fail boolean or array of booleans 4685 */ 4686 removeClass: function(el, className) { 4687 return Y.Dom.batch(el, Y.Dom._removeClass, className); 4688 }, 4689 4690 _removeClass: function(el, className) { 4691 var ret = false, 4692 current, 4693 newClass, 4694 attr; 4695 4696 if (el && className) { 4697 current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; 4698 Y.Dom.setAttribute(el, CLASS_NAME, current.replace(Y.Dom._getClassRegex(className), EMPTY)); 4699 4700 newClass = Y.Dom._getAttribute(el, CLASS_NAME); 4701 if (current !== newClass) { // else nothing changed 4702 Y.Dom.setAttribute(el, CLASS_NAME, trim(newClass)); // trim after comparing to current class 4703 ret = true; 4704 4705 if (Y.Dom._getAttribute(el, CLASS_NAME) === '') { // remove class attribute if empty 4706 attr = (el.hasAttribute && el.hasAttribute(_CLASS)) ? _CLASS : CLASS_NAME; 4707 YAHOO.log('removeClass removing empty class attribute', 'info', 'Dom'); 4708 el.removeAttribute(attr); 4709 } 4710 } 4711 4712 } else {
4713 YAHOO.log('removeClass called with invalid arguments', 'warn', 'Dom'); 4714 } 4715 4716 return ret; 4717 }, 4718 4719 /** 4720 * Replace a class with another class for a given element or collection of elements. 4721 * If no oldClassName is present, the newClassName is simply added. 4722 * @method replaceClass 4723 * @param {String | HTMLElement | Array} el The element or collection to remove the class from 4724 * @param {String} oldClassName the class name to be replaced 4725 * @param {String} newClassName the class name that will be replacing the old class name 4726 * @return {Boolean | Array} A pass/fail boolean or array of booleans 4727 */ 4728 replaceClass: function(el, oldClassName, newClassName) { 4729 return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName }); 4730 }, 4731 4732 _replaceClass: function(el, classObj) { 4733 var className, 4734 from, 4735 to, 4736 ret = false, 4737 current; 4738 4739 if (el && classObj) { 4740 from = classObj.from; 4741 to = classObj.to; 4742 4743 if (!to) { 4744 ret = false;
4745 } else if (!from) { // just add if no "from" 4746 ret = Y.Dom._addClass(el, classObj.to); 4747 } else if (from !== to) { // else nothing to replace 4748 // May need to lead with DBLSPACE? 4749 current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; 4750 className = (SPACE + current.replace(Y.Dom._getClassRegex(from), SPACE + to). 4751 replace(/\s+/g, SPACE)). // normalize white space 4752 split(Y.Dom._getClassRegex(to)); 4753 4754 // insert to into what would have been the first occurrence slot 4755 className.splice(1, 0, SPACE + to); 4756 Y.Dom.setAttribute(el, CLASS_NAME, trim(className.join(EMPTY))); 4757 ret = true; 4758 } 4759 } else { 4760 YAHOO.log('replaceClass called with invalid arguments', 'warn', 'Dom'); 4761 } 4762 4763 return ret; 4764 }, 4765 4766 /** 4767 * Returns an ID and applies it to the element "el", if provided. 4768 * @method generateId 4769 * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present). 4770 * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen"). 4771 * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element) 4772 */ 4773 generateId: function(el, prefix) { 4774 prefix = prefix || 'yui-gen'; 4775 4776 var f = function(el) { 4777 if (el && el.id) { // do not override existing ID 4778 YAHOO.log('generateId returning existing id ' + el.id, 'info', 'Dom'); 4779 return el.id; 4780 } 4781 4782 var id = prefix + YAHOO.env._id_counter++; 4783 YAHOO.log('generateId generating ' + id, 'info', 'Dom'); 4784 4785 if (el) { 4786 if (el[OWNER_DOCUMENT] && el[OWNER_DOCUMENT].getElementById(id)) { // in case one already exists 4787 // use failed id plus prefix to help ensure uniqueness 4788 return Y.Dom.generateId(el, id + prefix); 4789 } 4790 el.id = id; 4791 } 4792 4793 return id; 4794 }; 4795 4796 // batch fails when no element, so just generate and return single ID 4797 return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments); 4798 }, 4799 4800 /** 4801 * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy. 4802 * @method isAncestor 4803 * @param {String | HTMLElement} haystack The possible ancestor 4804 * @param {String | HTMLElement} needle The possible descendent 4805 * @return {Boolean} Whether or not the haystack is an ancestor of needle 4806 */ 4807 isAncestor: function(haystack, needle) { 4808 haystack = Y.Dom.get(haystack); 4809 needle = Y.Dom.get(needle); 4810 4811 var ret = false;
4812 4813 if ( (haystack && needle) && (haystack[NODE_TYPE] && needle[NODE_TYPE]) ) { 4814 if (haystack.contains && haystack !== needle) { // contains returns true when equal 4815 ret = haystack.contains(needle); 4816 } 4817 else if (haystack.compareDocumentPosition) { // gecko 4818 ret = !!(haystack.compareDocumentPosition(needle) & 16); 4819 } 4820 } else { 4821 YAHOO.log('isAncestor failed; invalid input: ' + haystack + ',' + needle, 'error', 'Dom'); 4822 } 4823 YAHOO.log('isAncestor(' + haystack + ',' + needle + ' returning ' + ret, 'info', 'Dom'); 4824 return ret; 4825 }, 4826 4827 /** 4828 * Determines whether an HTMLElement is present in the current document. 4829 * @method inDocument 4830 * @param {String | HTMLElement} el The element to search for 4831 * @param {Object} doc An optional document to search, defaults to element's owner document 4832 * @return {Boolean} Whether or not the element is present in the current document 4833 */ 4834 inDocument: function(el, doc) { 4835 return Y.Dom._inDoc(Y.Dom.get(el), doc); 4836 }, 4837 4838 _inDoc: function(el, doc) { 4839 var ret = false;
4840 if (el && el[TAG_NAME]) { 4841 doc = doc || el[OWNER_DOCUMENT]; 4842 ret = Y.Dom.isAncestor(doc[DOCUMENT_ELEMENT], el); 4843 } else { 4844 YAHOO.log('inDocument failed: invalid input', 'error', 'Dom'); 4845 } 4846 return ret; 4847 }, 4848 4849 /** 4850 * Returns an array of HTMLElements that pass the test applied by supplied boolean method. 4851 * For optimized performance, include a tag and/or root node when possible. 4852 * Note: This method operates against a live collection, so modifying the 4853 * collection in the callback (removing/appending nodes, etc.) will have 4854 * side effects. Instead you should iterate the returned nodes array, 4855 * as you would with the native "getElementsByTagName" method. 4856 * @method getElementsBy 4857 * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. 4858 * @param {String} tag (optional) The tag name of the elements being collected 4859 * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 4860 * @param {Function} apply (optional) A function to apply to each element when found 4861 * @param {Any} o (optional) An optional arg that is passed to the supplied method 4862 * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" 4863 * @return {Array} Array of HTMLElements 4864 */ 4865 getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) { 4866 tag = tag || '*'; 4867 root = (root) ? Y.Dom.get(root) : null || document; 4868 4869 var ret = (firstOnly) ? null : [], 4870 elements; 4871 4872 // in case Dom.get() returns null 4873 if (root) { 4874 elements = root.getElementsByTagName(tag); 4875 for (var i = 0, len = elements.length; i < len; ++i) { 4876 if ( method(elements[i]) ) { 4877 if (firstOnly) { 4878 ret = elements[i]; 4879 break; 4880 } else { 4881 ret[ret.length] = elements[i]; 4882 } 4883 } 4884 } 4885 4886 if (apply) { 4887 Y.Dom.batch(ret, apply, o, overrides); 4888 } 4889 } 4890 4891 YAHOO.log('getElementsBy returning ' + ret, 'info', 'Dom'); 4892 4893 return ret; 4894 }, 4895 4896 /** 4897 * Returns the first HTMLElement that passes the test applied by the supplied boolean method. 4898 * @method getElementBy 4899 * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. 4900 * @param {String} tag (optional) The tag name of the elements being collected 4901 * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 4902 * @return {HTMLElement} 4903 */ 4904 getElementBy: function(method, tag, root) { 4905 return Y.Dom.getElementsBy(method, tag, root, null, null, null, true); 4906 }, 4907 4908 /** 4909 * Runs the supplied method against each item in the Collection/Array. 4910 * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ). 4911 * @method batch 4912 * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to 4913 * @param {Function} method The method to apply to the element(s) 4914 * @param {Any} o (optional) An optional arg that is passed to the supplied method 4915 * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" 4916 * @return {Any | Array} The return value(s) from the supplied method 4917 */ 4918 batch: function(el, method, o, overrides) { 4919 var collection = [], 4920 scope = (overrides) ? o : null; 4921 4922 el = (el && (el[TAG_NAME] || el.item)) ? el : Y.Dom.get(el);
4922 // skip get() when possible 4923 if (el && method) { 4924 if (el[TAG_NAME] || el.length === undefined) { // element or not array-like 4925 return method.call(scope, el, o); 4926 } 4927 4928 for (var i = 0; i < el.length; ++i) { 4929 collection[collection.length] = method.call(scope || el[i], el[i], o); 4930 } 4931 } else { 4932 YAHOO.log('batch called with invalid arguments', 'warn', 'Dom'); 4933 return false; 4934 } 4935 return collection; 4936 }, 4937 4938 /** 4939 * Returns the height of the document. 4940 * @method getDocumentHeight 4941 * @return {Int} The height of the actual document (which includes the body and its margin). 4942 */ 4943 getDocumentHeight: function() { 4944 var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight, 4945 h = Math.max(scrollHeight, Y.Dom.getViewportHeight()); 4946 4947 YAHOO.log('getDocumentHeight returning ' + h, 'info', 'Dom'); 4948 return h; 4949 }, 4950 4951 /** 4952 * Returns the width of the document. 4953 * @method getDocumentWidth 4954 * @return {Int} The width of the actual document (which includes the body and its margin). 4955 */ 4956 getDocumentWidth: function() { 4957 var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth, 4958 w = Math.max(scrollWidth, Y.Dom.getViewportWidth()); 4959 YAHOO.log('getDocumentWidth returning ' + w, 'info', 'Dom'); 4960 return w; 4961 }, 4962 4963 /** 4964 * Returns the current height of the viewport. 4965 * @method getViewportHeight 4966 * @return {Int} The height of the viewable area of the page (excludes scrollbars). 4967 */ 4968 getViewportHeight: function() { 4969 var height = self.innerHeight, // Safari, Opera 4970 mode = document[COMPAT_MODE]; 4971 4972 if ( (mode || isIE) && !isOpera ) { // IE, Gecko 4973 height = (mode == CSS1_COMPAT) ? 4974 documentElement.clientHeight : // Standards 4975 document.body.clientHeight; // Quirks 4976 } 4977 4978 YAHOO.log('getViewportHeight returning ' + height, 'info', 'Dom'); 4979 return height; 4980 }, 4981 4982 /** 4983 * Returns the current width of the viewport. 4984 * @method getViewportWidth 4985 * @return {Int} The width of the viewable area of the page (excludes scrollbars). 4986 */ 4987 4988 getViewportWidth: function() { 4989 var width = self.innerWidth, // Safari 4990 mode = document[COMPAT_MODE]; 4991 4992 if (mode || isIE) { // IE, Gecko, Opera 4993 width = (mode == CSS1_COMPAT) ? 4994 documentElement.clientWidth : // Standards 4995 document.body.clientWidth; // Quirks 4996 } 4997 YAHOO.log('getViewportWidth returning ' + width, 'info', 'Dom'); 4998 return width; 4999 }, 5000 5001 /** 5002 * Returns the nearest ancestor that passes the test applied by supplied boolean method. 5003 * For performance reasons, IDs are not accepted and argument validation omitted. 5004 * @method getAncestorBy 5005 * @param {HTMLElement} node The HTMLElement to use as the starting point 5006 * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. 5007 * @return {Object} HTMLElement or null if not found 5008 */ 5009 getAncestorBy: function(node, method) { 5010 while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment 5011 if ( Y.Dom._testElement(node, method) ) { 5012 YAHOO.log('getAncestorBy returning ' + node, 'info', 'Dom'); 5013 return node; 5014 } 5015 } 5016 5017 YAHOO.log('getAncestorBy returning null (no ancestor passed test)', 'error', 'Dom'); 5018 return null; 5019 }, 5020 5021 /** 5022 * Returns the nearest ancestor with the given className. 5023 * @method getAncestorByClassName 5024 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5025 * @param {String} className 5026 * @return {Object} HTMLElement 5027 */ 5028 getAncestorByClassName: function(node, className) { 5029 node = Y.Dom.get(node); 5030 if (!node) { 5031 YAHOO.log('getAncestorByClassName failed: invali
5031d node argument', 'error', 'Dom'); 5032 return null; 5033 } 5034 var method = function(el) { return Y.Dom.hasClass(el, className); }; 5035 return Y.Dom.getAncestorBy(node, method); 5036 }, 5037 5038 /** 5039 * Returns the nearest ancestor with the given tagName. 5040 * @method getAncestorByTagName 5041 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5042 * @param {String} tagName 5043 * @return {Object} HTMLElement 5044 */ 5045 getAncestorByTagName: function(node, tagName) { 5046 node = Y.Dom.get(node); 5047 if (!node) { 5048 YAHOO.log('getAncestorByTagName failed: invalid node argument', 'error', 'Dom'); 5049 return null; 5050 } 5051 var method = function(el) { 5052 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase(); 5053 }; 5054 5055 return Y.Dom.getAncestorBy(node, method); 5056 }, 5057 5058 /** 5059 * Returns the previous sibling that is an HTMLElement. 5060 * For performance reasons, IDs are not accepted and argument validation omitted. 5061 * Returns the nearest HTMLElement sibling if no method provided. 5062 * @method getPreviousSiblingBy 5063 * @param {HTMLElement} node The HTMLElement to use as the starting point 5064 * @param {Function} method A boolean function used to test siblings 5065 * that receives the sibling node being tested as its only argument 5066 * @return {Object} HTMLElement or null if not found 5067 */ 5068 getPreviousSiblingBy: function(node, method) { 5069 while (node) { 5070 node = node.previousSibling; 5071 if ( Y.Dom._testElement(node, method) ) { 5072 return node; 5073 } 5074 } 5075 return null; 5076 }, 5077 5078 /** 5079 * Returns the previous sibling that is an HTMLElement 5080 * @method getPreviousSibling 5081 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5082 * @return {Object} HTMLElement or null if not found 5083 */ 5084 getPreviousSibling: function(node) { 5085 node = Y.Dom.get(node); 5086 if (!node) { 5087 YAHOO.log('getPreviousSibling failed: invalid node argument', 'error', 'Dom'); 5088 return null; 5089 } 5090 5091 return Y.Dom.getPreviousSiblingBy(node); 5092 }, 5093 5094 /** 5095 * Returns the next HTMLElement sibling that passes the boolean method. 5096 * For performance reasons, IDs are not accepted and argument validation omitted. 5097 * Returns the nearest HTMLElement sibling if no method provided. 5098 * @method getNextSiblingBy 5099 * @param {HTMLElement} node The HTMLElement to use as the starting point 5100 * @param {Function} method A boolean function used to test siblings 5101 * that receives the sibling node being tested as its only argument 5102 * @return {Object} HTMLElement or null if not found 5103 */ 5104 getNextSiblingBy: function(node, method) { 5105 while (node) { 5106 node = node.nextSibling; 5107 if ( Y.Dom._testElement(node, method) ) { 5108 return node; 5109 } 5110 } 5111 return null; 5112 }, 5113 5114 /** 5115 * Returns the next sibling that is an HTMLElement 5116 * @method getNextSibling 5117 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5118 * @return {Object} HTMLElement or null if not found 5119 */ 5120 getNextSibling: function(node) { 5121 node = Y.Dom.get(node); 5122 if (!node) { 5123 YAHOO.log('getNextSibling failed: invalid node argument', 'error', 'Dom'); 5124 return null; 5125 } 5126 5127 return Y.Dom.getNextSiblingBy(node); 5128 }, 5129 5130 /** 5131 * Returns the first HTMLElement child that passes the test method. 5132 * @method getFirstChildBy 5133 * @param {HTMLElement} node The HTMLElement to use as the starting point 5134 * @param {Function} method A boolean function used to test children 5135 * that receives the node being tested as its only argument 5136 * @return {Object} HTMLElement or null if not found 5137 */ 5138 getFirstChildBy: function(node, method) { 5139 var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null; 5140 return child || Y.Dom.getNextSiblingBy(node.firstChild, method); 5141 }, 5142 5143 /** 5144 * Returns the first HTMLElement child. 5145 * @method getFirstChild 5146 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5147 * @return {Object} HTMLElement or null if not found 5148 */ 5149 getFirstChild: function(node, method) { 5150 node = Y.Dom.get(node); 5151 if (!node) {
5152 YAHOO.log('getFirstChild failed: invalid node argument', 'error', 'Dom'); 5153 return null; 5154 } 5155 return Y.Dom.getFirstChildBy(node); 5156 }, 5157 5158 /** 5159 * Returns the last HTMLElement child that passes the test method. 5160 * @method getLastChildBy 5161 * @param {HTMLElement} node The HTMLElement to use as the starting point 5162 * @param {Function} method A boolean function used to test children 5163 * that receives the node being tested as its only argument 5164 * @return {Object} HTMLElement or null if not found 5165 */ 5166 getLastChildBy: function(node, method) { 5167 if (!node) { 5168 YAHOO.log('getLastChild failed: invalid node argument', 'error', 'Dom'); 5169 return null; 5170 } 5171 var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null; 5172 return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method); 5173 }, 5174 5175 /** 5176 * Returns the last HTMLElement child. 5177 * @method getLastChild 5178 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5179 * @return {Object} HTMLElement or null if not found 5180 */ 5181 getLastChild: function(node) { 5182 node = Y.Dom.get(node); 5183 return Y.Dom.getLastChildBy(node); 5184 }, 5185 5186 /** 5187 * Returns an array of HTMLElement childNodes that pass the test method. 5188 * @method getChildrenBy 5189 * @param {HTMLElement} node The HTMLElement to start from 5190 * @param {Function} method A boolean function used to test children 5191 * that receives the node being tested as its only argument 5192 * @return {Array} A static array of HTMLElements 5193 */ 5194 getChildrenBy: function(node, method) { 5195 var child = Y.Dom.getFirstChildBy(node, method), 5196 children = child ? [child] : []; 5197 5198 Y.Dom.getNextSiblingBy(child, function(node) { 5199 if ( !method || method(node) ) { 5200 children[children.length] = node; 5201 } 5202 return false; // fail test to collect all children 5203 }); 5204 5205 return children; 5206 }, 5207 5208 /** 5209 * Returns an array of HTMLElement childNodes. 5210 * @method getChildren 5211 * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 5212 * @return {Array} A static array of HTMLElements 5213 */ 5214 getChildren: function(node) { 5215 node = Y.Dom.get(node); 5216 if (!node) { 5217 YAHOO.log('getChildren failed: invalid node argument', 'error', 'Dom'); 5218 } 5219 5220 return Y.Dom.getChildrenBy(node); 5221 }, 5222 5223 /** 5224 * Returns the left scroll value of the document 5225 * @method getDocumentScrollLeft 5226 * @param {HTMLDocument} document (optional) The document to get the scroll value of 5227 * @return {Int} The amount that the document is scrolled to the left 5228 */ 5229 getDocumentScrollLeft: function(doc) { 5230 doc = doc || document; 5231 return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft); 5232 }, 5233 5234 /** 5235 * Returns the top scroll value of the document 5236 * @method getDocumentScrollTop 5237 * @param {HTMLDocument} document (optional) The document to get the scroll value of 5238 * @return {Int} The amount that the document is scrolled to the top 5239 */ 5240 getDocumentScrollTop: function(doc) { 5241 doc = doc || document; 5242 return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop); 5243 }, 5244 5245 /** 5246 * Inserts the new node as the previous sibling of the reference node 5247 * @method insertBefore 5248 * @param {String | HTMLElement} newNode The node to be inserted 5249 * @param {String | HTMLElement} referenceNode The node to insert the new node before 5250 * @return {HTMLElement} The node that was inserted (or null if insert fails) 5251 */ 5252 insertBefore: function(newNode, referenceNode) { 5253 newNode = Y.Dom.get(newNode); 5254 referenceNode = Y.Dom.get(referenceNode); 5255 5256 if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) { 5257 YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom'); 5258 return null; 5259 } 5260 5261 return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode); 5262 }, 5263 5264 /** 5265 * Inserts the new node as the next sibling of the reference node 5266 * @method insertAfter 5267 * @param {String | HTMLElement} newNode The node to be inserted 5268 * @param {String | HTMLElement} referenceNode The node to insert the new node after 5269 * @return {HTMLElement} The node that was inserted (or null if insert fails) 5270 */ 5271 insertAfter: function(newNode, referenceNode) { 5272 newNode = Y.Dom.get(newNode); 5273 referenceNode = Y.Dom.get(referenceNode); 5274 5275 if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) { 5276 YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom'); 5277 return null; 5278 } 5279 5280 if (referenceNode.nextSibling) { 5281 return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling); 5282 } else { 5283 return referenceNode[PARENT_NODE].appendChild(newNode); 5284 } 5285 }, 5286 5287 /** 5288 * Creates a Region based on the viewport relative to the document. 5289 * @method getClientRegion 5290 * @return {Region} A Region object representing the viewport which accounts for document scroll 5291 */ 5292 getClientRegion: function() { 5293 var t = Y.Dom.getDocumentScrollTop(), 5294 l = Y.Dom.getDocumentScrollLeft(), 5295 r = Y.Dom.getViewportWidth() + l,
5296 b = Y.Dom.getViewportHeight() + t; 5297 5298 return new Y.Region(t, r, b, l); 5299 }, 5300 5301 /** 5302 * Provides a normalized attribute interface. 5303 * @method setAttribute 5304 * @param {String | HTMLElement} el The target element for the attribute. 5305 * @param {String} attr The attribute to set. 5306 * @param {String} val The value of the attribute. 5307 */ 5308 setAttribute: function(el, attr, val) { 5309 Y.Dom.batch(el, Y.Dom._setAttribute, { attr: attr, val: val }); 5310 }, 5311 5312 _setAttribute: function(el, args) { 5313 var attr = Y.Dom._toCamel(args.attr), 5314 val = args.val; 5315 5316 if (el && el.setAttribute) { 5317 // set as DOM property, except for BUTTON, which errors on property setter 5318 if (Y.Dom.DOT_ATTRIBUTES[attr] && el.tagName && el.tagName != 'BUTTON') { 5319 el[attr] = val; 5320 } else { 5321 attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr; 5322 el.setAttribute(attr, val); 5323 } 5324 } else { 5325 YAHOO.log('setAttribute method not available for ' + el, 'error', 'Dom'); 5326 } 5327 }, 5328 5329 /** 5330 * Provides a normalized attribute interface. 5331 * @method getAttribute 5332 * @param {String | HTMLElement} el The target element for the attribute. 5333 * @param {String} attr The attribute to get. 5334 * @return {String} The current value of the attribute. 5335 */ 5336 getAttribute: function(el, attr) { 5337 return Y.Dom.batch(el, Y.Dom._getAttribute, attr); 5338 }, 5339 5340 5341 _getAttribute: function(el, attr) { 5342 var val; 5343 attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr; 5344 5345 if (Y.Dom.DOT_ATTRIBUTES[attr]) { 5346 val = el[attr]; 5347 } else if (el && 'getAttribute' in el) { 5348 if (/^(?:href|src)$/.test(attr)) { // use IE flag to return exact value 5349 val = el.getAttribute(attr, 2); 5350 } else { 5351 val = el.getAttribute(attr); 5352 } 5353 } else { 5354 YAHOO.log('getAttribute method not available for ' + el, 'error', 'Dom'); 5355 } 5356 5357 return val; 5358 }, 5359 5360 _toCamel: function(property) { 5361 var c = propertyCache; 5362 5363 function tU(x,l) { 5364 return l.toUpperCase(); 5365 } 5366 5367 return c[property] || (c[property] = property.indexOf('-') === -1 ? 5368 property : 5369 property.replace( /-([a-z])/gi, tU )); 5370 }, 5371 5372 _getClassRegex: function(className) { 5373 var re; 5374 if (className !== undefined) { // allow empty string to pass 5375 if (className.exec) { // already a RegExp 5376 re = className; 5377 } else { 5378 re = reCache[className]; 5379 if (!re) { 5380 // escape special chars (".", "[", etc.) 5381 className = className.replace(Y.Dom._patterns.CLASS_RE_TOKENS, '\\$1'); 5382 className = className.replace(/\s+/g, SPACE); // convert line breaks and other delims 5383 re = reCache[className] = new RegExp(C_START + className + C_END, G); 5384 } 5385 } 5386 } 5387 return re; 5388 }, 5389 5390 _patterns: { 5391 ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards, 5392 CLASS_RE_TOKENS: /([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g 5393 }, 5394 5395 5396 _testElement: function(node, method) { 5397 return node && node[NODE_TYPE] == 1 && ( !method || method(node) ); 5398 }, 5399 5400 _calcBorders: function(node, xy2) { 5401 var t = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0, 5402 l = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0; 5403 if (isGecko) { 5404 if (RE_TABLE.test(node[TAG_NAME])) { 5405 t = 0; 5406 l = 0; 5407 } 5408 } 5409 xy2[0] += l; 5410 xy2[1] += t; 5411 return xy2; 5412 } 5413 }; 5414 5415 var _getComputedStyle = Y.Dom[GET_COMPUTED_STYLE]; 5416 // fix opera computedStyle default color unit (convert to rgb) 5417 if (UA.opera) { 5418 Y.Dom[GET_COMPUTED_STYLE] = function(node, att) { 5419 var val = _getComputedStyle(node, att); 5420 if (RE_COLOR.test(att)) { 5421 val = Y.Dom.Color.toRGB(val); 5422 } 5423 5424 return val; 5425 }; 5426 5427 } 5428 5429 // safari converts transparent to rgba(), others use "transparent" 5430 if (UA.webkit) { 5431 Y.Dom[GET_COMPUTED_STYLE] = function(node, att) { 5432 var val = _getComputedStyle(node, att); 5433 5434 if (val === 'rgba(0, 0, 0, 0)') { 5435 val = 'transparent'; 5436 } 5437 5438 return val; 5439 }; 5440 5441 } 5442 5443 if (UA.ie && UA.ie >= 8) { 5444 Y.Dom.DOT_ATTRIBUTES.type = true; // IE 8 errors on input.setAttribute('type') 5445 } 5446})(); 5447/** 5448 * A region is a representation of an object on a grid. It is defined 5449 * by the top, right, bottom, left extents, so is rectangular by default. If 5450 * other shapes are required, this class could be extended to support it. 5451 * @namespace YAHOO.util 5452 * @class Region 5453 * @param {Int} t the top extent 5454 * @param {Int} r the right extent 5455 * @param {Int} b the bottom extent 5456 * @param {Int} l the left extent 5457 * @constructor 5458 */ 5459YAHOO.util.Region = function(t, r, b, l) { 5460 5461 /** 5462 * The region's top extent 5463 * @property top 5464 * @type Int 5465 */ 5466 this.top = t; 5467 5468 /** 5469 * The region's top extent 5470 * @property y 5471 * @type Int 5472 */ 5473 this.y = t; 5474 5475 /** 5476 * The region's top extent as index, for symmetry with set/getXY 5477 * @property 1 5478 * @type Int 5479 */ 5480 this[1] = t; 5481 5482 /** 5483 * The region's right extent 5484 * @property right 5485 * @type int 5486 */ 5487 this.right = r; 5488 5489 /** 5490 * The region's bottom extent 5491 * @property bottom 5492 * @type Int 5493 */ 5494 this.bottom = b; 5495 5496 /** 5497 * The region's left extent 5498 * @property left 5499 * @type Int 5500 */ 5501 this.left = l; 5502 5503 /** 5504 * The region's left extent 5505 * @property x 5506 * @type Int 5507 */ 5508 this.x = l; 5509 5510 /** 5511 * The region's left extent as index, for symmetry with set/getXY 5512 * @property 0 5513 * @type Int 5514 */ 5515 this[0] = l; 5516 5517 /** 5518 * The region's total width 5519 * @property width 5520 * @type Int 5521 */ 5522 this.width = this.right - this.left; 5523 5524 /** 5525 * The region's total height 5526 * @property height 5527 * @type Int 5528 */ 5529 this.height = this.bottom - this.top; 5530}; 5531 5532/** 5533 * Returns true if this region contains the region passed in 5534 * @method contains 5535 * @param {Region} region The region to evaluate 5536 * @return {Boolean} True if the region is contained with this region, 5537 * else false 5538 */ 5539YAHOO.util.Region.prototype.contains = function(region) { 5540 return ( region.left >= this.left && 5541 region.right <= this.right && 5542 region.top >= this.top && 5543 region.bottom <= this.bottom ); 5544 5545 // this.logger.debug("does " + this + " contain " + region + " ... " + ret); 5546}; 5547 5548/** 5549 * Returns the area of the region 5550 * @method getArea 5551 * @return {Int} the region's area 5552 */ 5553YAHOO.util.Region.prototype.getArea = function() { 5554 return ( (this.bottom - this.top) * (this.right - this.left) ); 5555}; 5556 5557/** 5558 * Returns the region where the passed in region overlaps with this one 5559 * @method intersect 5560 * @param {Region} region The region that intersects 5561 * @return {Region} The overlap region, or null if there is no overlap 5562 */ 5563YAHOO.util.Region.prototype.intersect = function(region) { 5564 var t = Math.max( this.top, region.top ), 5565 r = Math.min( this.right, region.right ), 5566 b = Math.min( this.bottom, region.bottom ), 5567 l = Math.max( this.left, region.left ); 5568 5569 if (b >= t && r >= l) { 5570 return new YAHOO.util.Region(t, r, b, l); 5571 } else { 5572 return null; 5573 } 5574}; 5575 5576/** 5577 * Returns the region representing the smallest region that can contain both 5578 * the passed in region and this region. 5579 * @method union 5580 * @param {Region} region The region that to create the union with 5581 * @return {Region}
5581 The union region 5582 */ 5583YAHOO.util.Region.prototype.union = function(region) { 5584 var t = Math.min( this.top, region.top ), 5585 r = Math.max( this.right, region.right ), 5586 b = Math.max( this.bottom, region.bottom ), 5587 l = Math.min( this.left, region.left ); 5588 5589 return new YAHOO.util.Region(t, r, b, l); 5590}; 5591 5592/** 5593 * toString 5594 * @method toString 5595 * @return string the region properties 5596 */ 5597YAHOO.util.Region.prototype.toString = function() { 5598 return ( "Region {" + 5599 "top: " + this.top + 5600 ", right: " + this.right + 5601 ", bottom: " + this.bottom + 5602 ", left: " + this.left + 5603 ", height: " + this.height + 5604 ", width: " + this.width + 5605 "}" ); 5606}; 5607 5608/** 5609 * Returns a region that is occupied by the DOM element 5610 * @method getRegion 5611 * @param {HTMLElement} el The element 5612 * @return {Region} The region that the element occupies 5613 * @static 5614 */ 5615YAHOO.util.Region.getRegion = function(el) { 5616 var p = YAHOO.util.Dom.getXY(el), 5617 t = p[1], 5618 r = p[0] + el.offsetWidth, 5619 b = p[1] + el.offsetHeight, 5620 l = p[0]; 5621 5622 return new YAHOO.util.Region(t, r, b, l); 5623}; 5624 5625///////////////////////////////////////////////////////////////////////////// 5626 5627 5628/** 5629 * A point is a region that is special in that it represents a single point on 5630 * the grid. 5631 * @namespace YAHOO.util 5632 * @class Point 5633 * @param {Int} x The X position of the point 5634 * @param {Int} y The Y position of the point 5635 * @constructor 5636 * @extends YAHOO.util.Region 5637 */ 5638YAHOO.util.Point = function(x, y) { 5639 if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc. 5640 y = x[1]; // dont blow away x yet 5641 x = x[0]; 5642 } 5643 5644 YAHOO.util.Point.superclass.constructor.call(this, y, x, y, x); 5645}; 5646 5647YAHOO.extend(YAHOO.util.Point, YAHOO.util.Region); 5648 5649(function() { 5650/** 5651 * Internal methods used to add style management functionality to DOM. 5652 * @module dom 5653 * @class IEStyle 5654 * @namespace YAHOO.util.Dom 5655 */ 5656 5657var Y = YAHOO.util, 5658 CLIENT_TOP = 'clientTop', 5659 CLIENT_LEFT = 'clientLeft', 5660 PARENT_NODE = 'parentNode', 5661 RIGHT = 'right', 5662 HAS_LAYOUT = 'hasLayout', 5663 PX = 'px', 5664 OPACITY = 'opacity', 5665 AUTO = 'auto', 5666 BORDER_LEFT_WIDTH = 'borderLeftWidth', 5667 BORDER_TOP_WIDTH = 'borderTopWidth', 5668 BORDER_RIGHT_WIDTH = 'borderRightWidth', 5669 BORDER_BOTTOM_WIDTH = 'borderBottomWidth', 5670 VISIBLE = 'visible', 5671 TRANSPARENT = 'transparent', 5672 HEIGHT = 'height', 5673 WIDTH = 'width', 5674 STYLE = 'style', 5675 CURRENT_STYLE = 'currentStyle', 5676 5677// IE getComputedStyle 5678// TODO: unit-less lineHeight (e.g. 1.22) 5679 re_size = /^width|height$/, 5680 re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i, 5681 5682 ComputedStyle = { 5683 /** 5684 * @method get 5685 * @description Method used by DOM to get style information for IE 5686 * @param {HTMLElement} el The element to check 5687 * @param {String} property The property to check 5688 * @returns {String} The computed style 5689 */ 5690 get: function(el, property) { 5691 var value = '', 5692 current = el[CURRENT_STYLE][property]; 5693 5694 if (property === OPACITY) { 5695 value = Y.Dom.getStyle(el, OPACITY); 5696 } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert 5697 value = current; 5698 } else if (Y.Dom.IE_COMPUTED[property]) { // use compute function 5699 value = Y.Dom.IE_COMPUTED[property](el, property); 5700 } else if (re_unit.test(current)) { // convert to pixel 5701 value = Y.Dom.IE.ComputedStyle.getPixel(el, property); 5702 } else { 5703 value = current; 5704 } 5705 5706 return value; 5707 }, 5708 /** 5709 * @method getOffset 5710 * @description Determine the offset of an element 5711 * @param {HTMLElement} el The element to check 5712 * @param {String} prop The property to check. 5713 * @return {String} The offset 5714 */ 5715 getOffset: function(el, prop) { 5716 var current = el[CURRENT_STYLE][prop], // value of "width", "top", etc. 5717 capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc. 5718 offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc. 5719 pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc. 5720 value = '', 5721 actual; 5722 5723 if (current == AUTO) { 5724 actual = el[offset]; // offsetHeight/Top etc. 5725 if (actual === undefined) { // likely "right" or "bottom" 5726 value = 0; 5727 } 5728 5729 value = actual; 5730 if (re_size.test(prop)) { // account for box model diff 5731 el[STYLE][prop] = actual; 5732 if (el[offset] > actual) { 5733 // the difference is padding + border (works in Standards & Qu
5733irks modes) 5734 value = actual - (el[offset] - actual); 5735 } 5736 el[STYLE][prop] = AUTO; // revert to auto 5737 } 5738 } else { // convert units to px 5739 if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth) 5740 el[STYLE][prop] = current; // no style.pixelWidth if no style.width 5741 } 5742 value = el[STYLE][pixel]; 5743 } 5744 return value + PX; 5745 }, 5746 /** 5747 * @method getBorderWidth 5748 * @description Try to determine the width of an elements border 5749 * @param {HTMLElement} el The element to check 5750 * @param {String} property The property to check 5751 * @return {String} The elements border width 5752 */ 5753 getBorderWidth: function(el, property) { 5754 // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth) 5755 // clientTop/Left = borderWidth 5756 var value = null; 5757 if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout? 5758 el[STYLE].zoom = 1; // need layout to measure client 5759 } 5760 5761 switch(property) { 5762 case BORDER_TOP_WIDTH: 5763 value = el[CLIENT_TOP]; 5764 break; 5765 case BORDER_BOTTOM_WIDTH: 5766 value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP]; 5767 break; 5768 case BORDER_LEFT_WIDTH: 5769 value = el[CLIENT_LEFT]; 5770 break; 5771 case BORDER_RIGHT_WIDTH: 5772 value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT]; 5773 break; 5774 } 5775 return value + PX; 5776 }, 5777 /** 5778 * @method getPixel 5779 * @description Get the pixel value from a style property 5780 * @param {HTMLElement} node The element to check 5781 * @param {String} att The attribute to check 5782 * @return {String} The pixel value 5783 */ 5784 getPixel: function(node, att) { 5785 // use pixelRight to convert to px 5786 var val = null, 5787 styleRight = node[CURRENT_STYLE][RIGHT], 5788 current = node[CURRENT_STYLE][att]; 5789 5790 node[STYLE][RIGHT] = current; 5791 val = node[STYLE].pixelRight; 5792 node[STYLE][RIGHT] = styleRight; // revert 5793 5794 return val + PX; 5795 }, 5796 5797 /** 5798 * @method getMargin 5799 * @description Get the margin value from a style property 5800 * @param {HTMLElement} node The element to check 5801 * @param {String} att The attribute to check 5802 * @return {String} The margin value 5803 */ 5804 getMargin: function(node, att) { 5805 var val; 5806 if (node[CURRENT_STYLE][att] == AUTO) { 5807 val = 0 + PX; 5808 } else { 5809 val = Y.Dom.IE.ComputedStyle.getPixel(node, att); 5810 } 5811 return val; 5812 }, 5813 5814 /** 5815 * @method getVisibility 5816 * @description Get the visibility of an element 5817 * @param {HTMLElement} node The element to check 5818 * @param {String} att The attribute to check 5819 * @return {String} The value 5820 */ 5821 getVisibility: function(node, att) { 5822 var current; 5823 while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test 5824 node = node[PARENT_NODE]; 5825 } 5826 return (current) ? current[att] : VISIBLE; 5827 }, 5828 5829 /** 5830 * @method getColor 5831 * @description Get the color of an element 5832 * @param {HTMLElement} node The element to check 5833 * @param {String} att The attribute to check 5834 * @return {String} The value 5835 */ 5836 getColor: function(node, att) { 5837 return Y.Dom.Color.toRGB(node[CURRENT_STYLE][att]) || TRANSPARENT; 5838 }, 5839 5840 /** 5841 * @method getBorderColor 5842 * @description Get the bordercolor of an element 5843 * @param {HTMLElement} node The element to check 5844 * @param {String} att The attribute to check 5845 * @return {String} The value 5846 */ 5847 getBorderColor: function(node, att) { 5848 var current = node[CURRENT_STYLE], 5849 val = current[att] || current.color; 5850 return Y.Dom.Color.toRGB(Y.Dom.Color.toHex(val)); 5851 } 5852 5853 }, 5854 5855//fontSize: getPixelFont, 5856 IEComputed = {}; 5857 5858IEComputed.top = IEComputed.right = IEComputed.bottom = IEComputed.left = 5859 IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; 5860 5861IEComputed.color = ComputedStyle.getColor; 5862 5863IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] = 5864 IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] = 5865 ComputedStyle.getBorderWidth; 5866 5867IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom = 5868 IEComputed.marginLeft = ComputedStyle.getMargin; 5869 5870IEComputed.visibility = ComputedStyle.getVisibility; 5871IEComputed.borderColor = IEComputed.borderTopColor = 5872 IEComputed.borderRightColor = IEComputed.borderBottomColor = 5873 IEComputed.borderLeftColor = ComputedStyle.getBorderColor; 5874 5875Y.Dom.IE_COMPUTED = IEComputed; 5876Y.Dom.IE_ComputedStyle = ComputedStyle; 5877})(); 5878(function() { 5879/** 5880 * Add style management functionality to DOM. 5881 * @module dom 5882 * @class Color 5883 * @namespace YAHOO.util.Dom 5884 */ 5885 5886var TO_STRING = 'toString', 5887 PARSE_INT = parseInt, 5888 RE = RegExp, 5889 Y = YAHOO.util; 5890 5891Y.Dom.Color = { 5892 /** 5893 * @property KEYWORDS 5894 * @type Object
5895 * @description Color keywords used when converting to Hex 5896 */ 5897 KEYWORDS: { 5898 black: '000', 5899 silver: 'c0c0c0', 5900 gray: '808080', 5901 white: 'fff', 5902 maroon: '800000', 5903 red: 'f00', 5904 purple: '800080', 5905 fuchsia: 'f0f', 5906 green: '008000', 5907 lime: '0f0', 5908 olive: '808000', 5909 yellow: 'ff0', 5910 navy: '000080', 5911 blue: '00f', 5912 teal: '008080', 5913 aqua: '0ff' 5914 }, 5915 /** 5916 * @property re_RGB 5917 * @private 5918 * @type Regex 5919 * @description Regex to parse rgb(0,0,0) formatted strings 5920 */ 5921 re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i, 5922 /** 5923 * @property re_hex 5924 * @private 5925 * @type Regex 5926 * @description Regex to parse #123456 formatted strings 5927 */ 5928 re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i, 5929 /** 5930 * @property re_hex3 5931 * @private 5932 * @type Regex 5933 * @description Regex to parse #123 formatted strings 5934 */ 5935 re_hex3: /([0-9A-F])/gi, 5936 /** 5937 * @method toRGB 5938 * @description Converts a hex or color string to an rgb string: rgb(0,0,0) 5939 * @param {String} val The string to convert to RGB notation. 5940 * @returns {String} The converted string 5941 */ 5942 toRGB: function(val) { 5943 if (!Y.Dom.Color.re_RGB.test(val)) { 5944 val = Y.Dom.Color.toHex(val); 5945 } 5946 5947 if(Y.Dom.Color.re_hex.exec(val)) { 5948 val = 'rgb(' + [ 5949 PARSE_INT(RE.$1, 16), 5950 PARSE_INT(RE.$2, 16), 5951 PARSE_INT(RE.$3, 16) 5952 ].join(', ') + ')'; 5953 } 5954 return val; 5955 }, 5956 /** 5957 * @method toHex 5958 * @description Converts an rgb or color string to a hex string: #123456 5959 * @param {String} val The string to convert to hex notation. 5960 * @returns {String} The converted string 5961 */ 5962 toHex: function(val) { 5963 val = Y.Dom.Color.KEYWORDS[val] || val; 5964 if (Y.Dom.Color.re_RGB.exec(val)) { 5965 val = [ 5966 Number(RE.$1).toString(16), 5967 Number(RE.$2).toString(16), 5968 Number(RE.$3).toString(16) 5969 ]; 5970 5971 for (var i = 0; i < val.length; i++) { 5972 if (val[i].length < 2) { 5973 val[i] = '0' + val[i]; 5974 } 5975 } 5976 5977 val = val.join(''); 5978 } 5979 5980 if (val.length < 6) { 5981 val = val.replace(Y.Dom.Color.re_hex3, '$1$1'); 5982 } 5983 5984 if (val !== 'transparent' && val.indexOf('#') < 0) { 5985 val = '#' + val; 5986 } 5987 5988 return val.toUpperCase(); 5989 } 5990}; 5991}()); 5992YAHOO.register("dom", YAHOO.util.Dom, {version: "2.9.0", build: "2800"}); 5993/* 5994Copyright (c) 2011, Yahoo! Inc. All rights reserved. 5995Code licensed under the BSD License: 5996http://developer.yahoo.com/yui/license.html 5997version: 2.9.0 5998*/ 5999/** 6000 * The drag and drop utility provides a framework for building drag and drop 6001 * applications. In addition to enabling drag and drop for specific elements, 6002 * the drag and drop elements are tracked by the manager class, and the 6003 * interactions between the various elements are tracked during the drag and 6004 * the implementing code is notified about these important moments. 6005 * @module dragdrop 6006 * @title Drag and Drop 6007 * @requires yahoo,dom,event 6008 * @namespace YAHOO.util 6009 */ 6010 6011// Only load the library once. Rewriting the manager class would orphan 6012// existing drag and drop instances. 6013if (!YAHOO.util.DragDropMgr) { 6014 6015/** 6016 * DragDropMgr is a singleton that tracks the element interaction for 6017 * all DragDrop items in the window. Generally, you will not call 6018 * this class directly, but it does have helper methods that could 6019 * be useful in your DragDrop implementations. 6020 * @class DragDropMgr 6021 * @static 6022 */ 6023YAHOO.util.DragDropMgr = function() { 6024 6025 var Event = YAHOO.util.Event, 6026 Dom = YAHOO.util.Dom; 6027 6028 return { 6029 /** 6030 * This property is used to turn on global use of the shim element on all DragDrop instances, defaults to false for backcompat. (Use: YAHOO.util.DDM.useShim = true) 6031 * @property useShim 6032 * @type Boolean 6033 * @static 6034 */ 6035 useShim: false, 6036 /** 6037 * This property is used to determine if the shim is active over the screen, default false. 6038 * @private 6039 * @property _shimActive 6040 * @type Boolean 6041 * @static 6042 */ 6043 _shimActive: false, 6044 /** 6045 * This property is used when useShim is set on a DragDrop object to store the current state of DDM.useShim so it can be reset when a drag operation is done. 6046 * @private 6047 * @property _shimState 6048 * @type Boolean 6049 * @static 6050 */ 6051 _shimState: false, 6052 /** 6053 * This property is used when useShim is set to true, it will set the opacity on the shim to .5 for debugging. Use: (YAHOO.util.DDM._debugShim = true;) 6054 * @private 6055 * @property _debugShim 6056 * @type Boolean 6057 * @static 6058 */ 6059 _debugShim: false, 6060 /** 6061 * This method will create a shim element (giving it the id of yui-ddm-shim), it also attaches the mousemove and mouseup listeners to it and attaches a scroll listener on the window 6062 * @private 6063 * @method _sizeShim 6064 * @static 6065 */ 6066 _createShim: function() { 6067 YAHOO.log('Creating Shim Element', 'info', 'DragDropMgr'); 6068 var s = document.createElement('div'); 6069 s.id = 'yui-ddm-shim'; 6070 if (document.body.firstChild) { 6071 document.body.insertBefore(s, document.body.firstChild); 6072 } else { 6073 document.body.appendChild(s); 6074 } 6075 s.style.display = 'none'; 6076 s.style.backgroundColor = 'red'; 6077 s.style.position = 'absolute'; 6078 s.style.zIndex = '99999'; 6079 Dom.setStyle(s, 'opacity', '0'); 6080 this._shim = s; 6081 Event.on(s, "mouseup", this.handleMouseUp, this, true); 6082 Event.on(s, "mousemove", this.handleMouseMove, this, true); 6083 Event.on(window, 'scroll', this._sizeShim, this, true); 6084 }, 6085 /** 6086 * This method will size the shim, called from activate and on window scroll event 6087 * @private 6088 * @method _sizeShim 6089 * @static 6090 */ 6091 _sizeShim: function() { 6092 if (this._shimActive) { 6093 YAHOO.log('Sizing Shim', 'info', 'DragDropMgr'); 6094 var s = this._shim; 6095 s.style.height = Dom.getDocumentHeight() + 'px'; 6096 s.style.width = Dom.getDocumentWidth() + 'px'; 6097 s.style.top = '0'; 6098 s.style.left = '0'; 6099 } 6100 }, 6101 /** 6102 * This method will create the shim element if needed, then show the shim element, size the element and set the _shimActive property to true 6103 * @private 6104 * @method _activateShim 6105 * @static 6106 */ 6107 _activateShim: function() { 6108 if (this.useShim) { 6109 YAHOO.log('Activating Shim', 'info', 'DragDropMgr'); 6110 if (!this._shim) { 6111 this._createShim(); 6112 } 6113 this._shimActive = true; 6114 var s = this._shim, 6115 o = '0'; 6116 if (this._debugShim) { 6117 o = '.5'; 6118 } 6119 Dom.setStyle(s, 'opacity', o); 6120 this._sizeShim(); 6121 s.style.display = 'block'; 6122 } 6123 }, 6124 /** 6125 * This method will hide the shim element and set the _shimActive property to false 6126 * @private 6127 * @method _deactivateShim 6128 * @static 6129 */ 6130 _deactivateShim: function() { 6131 YAHOO.log('Deactivating Shim', 'info', 'DragDropMgr'); 6132 this._shim.style.display = 'none'; 6133 this._shimActive = false;
6134 }, 6135 /** 6136 * The HTML element created to use as a shim over the document to track mouse movements 6137 * @private 6138 * @property _shim 6139 * @type HTMLElement 6140 * @static 6141 */ 6142 _shim: null, 6143 /** 6144 * Two dimensional Array of registered DragDrop objects. The first 6145 * dimension is the DragDrop item group, the second the DragDrop 6146 * object. 6147 * @property ids 6148 * @type {string: string} 6149 * @private 6150 * @static 6151 */ 6152 ids: {}, 6153 6154 /** 6155 * Array of element ids defined as drag handles. Used to determine 6156 * if the element that generated the mousedown event is actually the 6157 * handle and not the html element itself. 6158 * @property handleIds 6159 * @type {string: string} 6160 * @private 6161 * @static 6162 */ 6163 handleIds: {}, 6164 6165 /** 6166 * the DragDrop object that is currently being dragged 6167 * @property dragCurrent 6168 * @type DragDrop 6169 * @private 6170 * @static 6171 **/ 6172 dragCurrent: null, 6173 6174 /** 6175 * the DragDrop object(s) that are being hovered over 6176 * @property dragOvers 6177 * @type Array 6178 * @private 6179 * @static 6180 */ 6181 dragOvers: {}, 6182 6183 /** 6184 * the X distance between the cursor and the object being dragged 6185 * @property deltaX 6186 * @type int 6187 * @private 6188 * @static 6189 */ 6190 deltaX: 0, 6191 6192 /** 6193 * the Y distance between the cursor and the object being dragged 6194 * @property deltaY 6195 * @type int 6196 * @private 6197 * @static 6198 */ 6199 deltaY: 0, 6200 6201 /** 6202 * Flag to determine if we should prevent the default behavior of the 6203 * events we define. By default this is true, but this can be set to 6204 * false if you need the default behavior (not recommended) 6205 * @property preventDefault 6206 * @type boolean 6207 * @static 6208 */ 6209 preventDefault: true, 6210 6211 /** 6212 * Flag to determine if we should stop the propagation of the events 6213 * we generate. This is true by default but you may want to set it to 6214 * false if the html element contains other features that require the 6215 * mouse click. 6216 * @property stopPropagation 6217 * @type boolean 6218 * @static 6219 */ 6220 stopPropagation: true, 6221 6222 /** 6223 * Internal flag that is set to true when drag and drop has been 6224 * initialized 6225 * @property initialized 6226 * @private 6227 * @static 6228 */ 6229 initialized: false, 6230 6231 /** 6232 * All drag and drop can be disabled. 6233 * @property locked 6234 * @private 6235 * @static 6236 */ 6237 locked: false, 6238 6239 /** 6240 * Provides additional information about the the current set of 6241 * interactions. Can be accessed from the event handlers. It 6242 * contains the following properties: 6243 * 6244 * out: onDragOut interactions 6245 * enter: onDragEnter interactions 6246 * over: onDragOver interactions 6247 * drop: onDragDrop interactions 6248 * point: The location of the cursor 6249 * draggedRegion: The location of dragged element at the time 6250 * of the interaction 6251 * sourceRegion: The location of the source elemtn at the time 6252 * of the interaction 6253 * validDrop: boolean 6254 * @property interactionInfo 6255 * @type object 6256 * @static 6257 */ 6258 interactionInfo: null, 6259 6260 /** 6261 * Called the first time an element is registered. 6262 * @method init 6263 * @private 6264 * @static 6265 */ 6266 init: function() { 6267 this.initialized = true; 6268 }, 6269 6270 /** 6271 * In point mode, drag and drop interaction is defined by the 6272 * location of the cursor during the drag/drop 6273 * @property POINT 6274 * @type int 6275 * @static 6276 * @final 6277 */ 6278 POINT: 0, 6279 6280 /** 6281 * In intersect mode, drag and drop interaction is defined by the 6282 * cursor position or the amount of overlap of two or more drag and 6283 * drop objects. 6284 * @property INTERSECT 6285 * @type int 6286 * @static 6287 * @final 6288 */ 6289 INTERSECT: 1, 6290 6291 /** 6292 * In intersect mode, drag and drop interaction is defined only by the 6293 * overlap of two or more drag and drop objects. 6294 * @property STRICT_INTERSECT 6295 * @type int 6296 * @static 6297 * @final 6298 */ 6299 STRICT_INTERSECT: 2, 6300 6301 /** 6302 * The current drag and drop mode. Default: POINT 6303 * @property mode 6304 * @type int 6305 * @static 6306 */ 6307 mode: 0, 6308 6309 /** 6310 * Runs method on all drag and drop objects 6311 * @method _execOnAll 6312 * @private 6313 * @static 6314 */ 6315 _execOnAll: function(sMethod, args) { 6316 for (var i in this.ids) { 6317 for (var j in this.ids[i]) { 6318 var oDD = this.ids[i][j]; 6319 if (! this.isTypeOfDD(oDD)) { 6320 continue; 6321 } 6322 oDD[sMethod].apply(oDD, args); 6323 } 6324 } 6325 }, 6326 6327 /** 6328 * Drag and drop initialization. Sets up the global event handlers 6329 * @method _onLoad 6330 * @private 6331 * @static 6332 */ 6333 _onLoad: function() { 6334 6335 this.init(); 6336 6337 YAHOO.log("DragDropMgr onload", "info", "DragDropMgr"); 6338 Event.on(document, "mouseup", this.handleMouseUp, this, true); 6339 Event.on(document, "mousemove", this.handleMouseMove, this, true); 6340 Event.on(window, "unload", this._onUnload, this, true); 6341 Event.on(window, "resize", this._onResize, this, true); 6342 // Event.on(window, "mouseout", this._test); 6343 6344 }, 6345 6346 /** 6347 * Reset constraints on all drag and drop objs 6348 * @method _onResize 6349 * @private 6350 * @static 6351 */ 6352 _onResize: function(e) { 6353 YAHOO.log("window resize", "info", "DragDropMgr"); 6354 this._execOnAll("resetConstraints", []); 6355 }, 6356 6357 /** 6358 * Lock all drag and drop functionality 6359 * @method lock 6360 * @static 6361 */ 6362 lock: function() { this.locked = true; }, 6363 6364 /** 6365 * Unlock all drag and drop functionality 6366 * @method unlock 6367 * @static 6368 */ 6369 unlock: function() { this.locked = false; }, 6370 6371 /** 6372 * Is drag and drop locked? 6373 * @method isLocked 6374 * @return {boolean} True if drag and drop is locked, false otherwise. 6375 * @static 6376 */ 6377 isLocked: function() { return this.locked; }, 6378 6379 /** 6380 * Location cache that is set for all drag drop objects when a drag is 6381 * initiated, cleared when the drag is finished. 6382 * @property locationCache 6383 * @private 6384 * @static 6385 */ 6386 locationCache: {}, 6387 6388 /** 6389 * Set useCache to false if you want to force object the lookup of each 6390 * drag and drop linked element constantly during a drag. 6391 * @property useCache 6392 * @type boolean 6393 * @static 6394 */ 6395 useCache: true, 6396 6397 /** 6398 * The number of pixels that the mouse needs to move after the 6399 * mousedown before the drag is initiated. Default=3; 6400 * @property clickPixelThresh 6401 * @type int 6402 * @static 6403 */ 6404 clickPixelThresh: 3, 6405 6406 /** 6407 * The number of milliseconds after the mousedown event to initiate the 6408 * drag if we don't get a mouseup event. Default=1000 6409 * @property clickTimeThresh 6410 * @type int 6411 * @static 6412 */ 6413 clickTimeThresh: 1000, 6414 6415 /** 6416 * Flag that indicates that either the drag pixel threshold or the 6417 * mousdown time threshold has been met 6418 * @property dragThreshMet 6419 * @type boolean 6420 * @private 6421 * @static 6422 */ 6423 dragThreshMet: false, 6424 6425 /** 6426 * Timeout used for the click time threshold 6427 * @property clickTimeout 6428 * @type Object 6429 * @private 6430 * @static 6431 */ 6432 clickTimeout: null, 6433 6434 /** 6435 * The X position of the mousedown event stored for later use when a 6436 * drag threshold is met. 6437 * @property startX 6438 * @type int 6439 * @private 6440 * @static 6441 */ 6442 startX: 0, 6443 6444 /** 6445 * The Y position of the mousedown event stored for later use when a 6446 * drag threshold is met. 6447 * @property startY 6448 * @type int 6449 * @private 6450 * @static 6451 */ 6452 startY: 0, 6453 6454 /** 6455 * Flag to determine if the drag event was fired from the click timeout and 6456 * not the mouse move threshold. 6457 * @property fromTimeout 6458 * @type boolean 6459 * @private 6460 * @static 6461 */ 6462 fromTimeout: false, 6463 6464 /** 6465 * Each DragDrop instance must be registered with the DragDropMgr. 6466 * This is executed in DragDrop.init() 6467 * @method regDragDrop 6468 * @param {DragDrop} oDD the DragDrop object to register 6469 * @param {String} sGroup the name of the group this element belongs to 6470 * @static 6471 */
6472 regDragDrop: function(oDD, sGroup) { 6473 if (!this.initialized) { this.init(); } 6474 6475 if (!this.ids[sGroup]) { 6476 this.ids[sGroup] = {}; 6477 } 6478 this.ids[sGroup][oDD.id] = oDD; 6479 }, 6480 6481 /** 6482 * Removes the supplied dd instance from the supplied group. Executed 6483 * by DragDrop.removeFromGroup, so don't call this function directly. 6484 * @method removeDDFromGroup 6485 * @private 6486 * @static 6487 */ 6488 removeDDFromGroup: function(oDD, sGroup) { 6489 if (!this.ids[sGroup]) { 6490 this.ids[sGroup] = {}; 6491 } 6492 6493 var obj = this.ids[sGroup]; 6494 if (obj && obj[oDD.id]) { 6495 delete obj[oDD.id]; 6496 } 6497 }, 6498 6499 /** 6500 * Unregisters a drag and drop item. This is executed in 6501 * DragDrop.unreg, use that method instead of calling this directly. 6502 * @method _remove 6503 * @private 6504 * @static 6505 */
6506 _remove: function(oDD) { 6507 for (var g in oDD.groups) { 6508 if (g) { 6509 var item = this.ids[g]; 6510 if (item && item[oDD.id]) { 6511 delete item[oDD.id]; 6512 } 6513 } 6514 6515 } 6516 delete this.handleIds[oDD.id]; 6517 }, 6518 6519 /** 6520 * Each DragDrop handle element must be registered. This is done 6521 * automatically when executing DragDrop.setHandleElId() 6522 * @method regHandle 6523 * @param {String} sDDId the DragDrop id this element is a handle for 6524 * @param {String} sHandleId the id of the element that is the drag 6525 * handle 6526 * @static 6527 */ 6528 regHandle: function(sDDId, sHandleId) { 6529 if (!this.handleIds[sDDId]) { 6530 this.handleIds[sDDId] = {}; 6531 } 6532 this.handleIds[sDDId][sHandleId] = sHandleId; 6533 }, 6534 6535 /** 6536 * Utility function to determine if a given element has been 6537 * registered as a drag drop item. 6538 * @method isDragDrop 6539 * @param {String} id the element id to check 6540 * @return {boolean} true if this element is a DragDrop item, 6541 * false otherwise 6542 * @static 6543 */ 6544 isDragDrop: function(id) { 6545 return ( this.getDDById(id) ) ? true : false; 6546 }, 6547 6548 /** 6549 * Returns the drag and drop instances that are in all groups the 6550 * passed in instance belongs to. 6551 * @method getRelated 6552 * @param {DragDrop} p_oDD the obj to get related data for 6553 * @param {boolean} bTargetsOnly if true, only return targetable objs 6554 * @return {DragDrop[]} the related instances 6555 * @static 6556 */ 6557 getRelated: function(p_oDD, bTargetsOnly) { 6558 var oDDs = []; 6559 for (var i in p_oDD.groups) { 6560 for (var j in this.ids[i]) { 6561 var dd = this.ids[i][j]; 6562 if (! this.isTypeOfDD(dd)) { 6563 continue; 6564 } 6565 if (!bTargetsOnly || dd.isTarget) { 6566 oDDs[oDDs.length] = dd; 6567 } 6568 } 6569 } 6570 6571 return oDDs; 6572 }, 6573 6574 /** 6575 * Returns true if the specified dd target is a legal target for 6576 * the specifice drag obj 6577 * @method isLegalTarget 6578 * @param {DragDrop} the drag obj 6579 * @param {DragDrop} the target 6580 * @return {boolean} true if the target is a legal target for the 6581 * dd obj 6582 * @static 6583 */ 6584 isLegalTarget: function (oDD, oTargetDD) { 6585 var targets = this.getRelated(oDD, true); 6586 for (var i=0, len=targets.length;i<len;++i) { 6587 if (targets[i].id == oTargetDD.id) { 6588 return true; 6589 } 6590 } 6591 6592 return false; 6593 }, 6594 6595 /** 6596 * My goal is to be able to transparently determine if an object is 6597 * typeof DragDrop, and the exact subclass of DragDrop. typeof 6598 * returns "object", oDD.constructor.toString() always returns 6599 * "DragDrop" and not the name of the subclass. So for now it just 6600 * evaluates a well-known variable in DragDrop. 6601 * @method isTypeOfDD 6602 * @param {Object} the object to evaluate 6603 * @return {boolean} true if typeof oDD = DragDrop 6604 * @static 6605 */ 6606 isTypeOfDD: function (oDD) { 6607 return (oDD && oDD.__ygDragDrop); 6608 }, 6609 6610 /** 6611 * Utility function to determine if a given element has been 6612 * registered as a drag drop handle for the given Drag Drop object. 6613 * @method isHandle 6614 * @param {String} id the element id to check 6615 * @return {boolean} true if this element is a DragDrop handle, false 6616 * otherwise 6617 * @static 6618 */ 6619 isHandle: function(sDDId, sHandleId) { 6620 return ( this.handleIds[sDDId] && 6621 this.handleIds[sDDId][sHandleId] ); 6622 }, 6623 6624 /** 6625 * Returns the DragDrop instance for a given id 6626 * @method getDDById 6627 * @param {String} id the id of the DragDrop object 6628 * @return {DragDrop} the drag drop object, null if it is not found 6629 * @static 6630 */ 6631 getDDById: function(id) { 6632 for (var i in this.ids) { 6633 if (this.ids[i][id]) { 6634 return this.ids[i][id]; 6635 } 6636 } 6637 return null; 6638 }, 6639 6640 /** 6641 * Fired after a registered DragDrop object gets the mousedown event. 6642 * Sets up the events required to track the object being dragged 6643 * @method handleMouseDown 6644 * @param {Event} e the event 6645 * @param oDD the DragDrop object being dragged 6646 * @private 6647 * @static 6648 */ 6649 handleMouseDown: function(e, oDD) { 6650 //this._activateShim(); 6651 6652 this.currentTarget = YAHOO.util.Event.getTarget(e); 6653 6654 this.dragCurrent = oDD; 6655 6656 var el = oDD.getEl(); 6657 6658 // track start position 6659 this.startX = YAHOO.util.Event.getPageX(e); 6660 this.startY = YAHOO.util.Event.getPageY(e); 6661 6662 this.deltaX = this.startX - el.offsetLeft; 6663 this.deltaY = this.startY - el.offsetTop; 6664 6665 this.dragThreshMet = false;
6666 6667 this.clickTimeout = setTimeout( 6668 function() { 6669 var DDM = YAHOO.util.DDM; 6670 DDM.startDrag(DDM.startX, DDM.startY); 6671 DDM.fromTimeout = true; 6672 }, 6673 this.clickTimeThresh ); 6674 }, 6675 6676 /** 6677 * Fired when either the drag pixel threshold or the mousedown hold 6678 * time threshold has been met. 6679 * @method startDrag 6680 * @param x {int} the X position of the original mousedown 6681 * @param y {int} the Y position of the original mousedown 6682 * @static 6683 */ 6684 startDrag: function(x, y) { 6685 if (this.dragCurrent && this.dragCurrent.useShim) { 6686 this._shimState = this.useShim; 6687 this.useShim = true; 6688 } 6689 this._activateShim(); 6690 YAHOO.log("firing drag start events", "info", "DragDropMgr"); 6691 clearTimeout(this.clickTimeout); 6692 var dc = this.dragCurrent; 6693 if (dc && dc.events.b4StartDrag) { 6694 dc.b4StartDrag(x, y); 6695 dc.fireEvent('b4StartDragEvent', { x: x, y: y }); 6696 } 6697 if (dc && dc.events.startDrag) { 6698 dc.startDrag(x, y); 6699 dc.fireEvent('startDragEvent', { x: x, y: y }); 6700 } 6701 this.dragThreshMet = true; 6702 }, 6703 6704 /** 6705 * Internal function to handle the mouseup event. Will be invoked 6706 * from the context of the document. 6707 * @method handleMouseUp 6708 * @param {Event} e the event 6709 * @private 6710 * @static 6711 */ 6712 handleMouseUp: function(e) { 6713 if (this.dragCurrent) { 6714 clearTimeout(this.clickTimeout); 6715 6716 if (this.dragThreshMet) { 6717 YAHOO.log("mouseup detected - completing drag", "info", "DragDropMgr"); 6718 if (this.fromTimeout) { 6719 YAHOO.log('fromTimeout is true (mouse didn\'t move), call handleMouseMove so we can get the dragOver event', 'info', 'DragDropMgr'); 6720 this.fromTimeout = false; 6721 this.handleMouseMove(e); 6722 } 6723 this.fromTimeout = false; 6724 this.fireEvents(e, true); 6725 } else { 6726 YAHOO.log("drag threshold not met", "info", "DragDropMgr"); 6727 } 6728 6729 this.stopDrag(e); 6730 6731 this.stopEvent(e); 6732 } 6733 }, 6734 6735 /** 6736 * Utility to stop event propagation and event default, if these 6737 * features are turned on. 6738 * @method stopEvent 6739 * @param {Event} e the event as returned by this.getEvent() 6740 * @static 6741 */ 6742 stopEvent: function(e) { 6743 if (this.stopPropagation) { 6744 YAHOO.util.Event.stopPropagation(e); 6745 } 6746 6747 if (this.preventDefault) { 6748 YAHOO.util.Event.preventDefault(e); 6749 } 6750 }, 6751 6752 /** 6753 * Ends the current drag, cleans up the state, and fires the endDrag 6754 * and mouseUp events. Called internally when a mouseup is detected 6755 * during the drag. Can be fired manually during the drag by passing 6756 * either another event (such as the mousemove event received in onDrag) 6757 * or a fake event with pageX and pageY defined (so that endDrag and 6758 * onMouseUp have usable position data.). Alternatively, pass true 6759 * for the silent parameter so that the endDrag and onMouseUp events 6760 * are skipped (so no event data is needed.) 6761 * 6762 * @method stopDrag 6763 * @param {Event} e the mouseup event, another event (or a fake event) 6764 * with pageX and pageY defined, or nothing if the 6765 * silent parameter is true 6766 * @param {boolean} silent skips the enddrag and mouseup events if true 6767 * @static 6768 */ 6769 stopDrag: function(e, silent) { 6770 // YAHOO.log("mouseup - removing event handlers"); 6771 var dc = this.dragCurrent; 6772 // Fire the drag end event for the item that was dragged 6773 if (dc && !silent) { 6774 if (this.dragThreshMet) { 6775 YAHOO.log("firing endDrag events", "info", "DragDropMgr"); 6776 if (dc.events.b4EndDrag) { 6777 dc.b4EndDrag(e); 6778 dc.fireEvent('b4EndDragEvent', { e: e }); 6779 } 6780 if (dc.events.endDrag) { 6781 dc.endDrag(e); 6782 dc.fireEvent('endDragEvent', { e: e }); 6783 } 6784 } 6785 if (dc.events.mouseUp) { 6786 YAHOO.log("firing dragdrop onMouseUp event", "info", "DragDropMgr"); 6787 dc.onMouseUp(e); 6788 dc.fireEvent('mouseUpEvent', { e: e }); 6789 } 6790 } 6791 6792 if (this._shimActive) { 6793 this._deactivateShim(); 6794 if (this.dragCurrent && this.dragCurrent.useShim) { 6795 this.useShim = this._shimState; 6796 this._shimState = false;
6797 } 6798 } 6799 6800 this.dragCurrent = null; 6801 this.dragOvers = {}; 6802 }, 6803 6804 /** 6805 * Internal function to handle the mousemove event. Will be invoked 6806 * from the context of the html element. 6807 * 6808 * @TODO figure out what we can do about mouse events lost when the 6809 * user drags objects beyond the window boundary. Currently we can 6810 * detect this in internet explorer by verifying that the mouse is 6811 * down during the mousemove event. Firefox doesn't give us the 6812 * button state on the mousemove event. 6813 * @method handleMouseMove 6814 * @param {Event} e the event 6815 * @private 6816 * @static 6817 */ 6818 handleMouseMove: function(e) { 6819 //YAHOO.log("handlemousemove"); 6820 6821 var dc = this.dragCurrent; 6822 if (dc) { 6823 // YAHOO.log("no current drag obj"); 6824 6825 // var button = e.which || e.button; 6826 // YAHOO.log("which: " + e.which + ", button: "+ e.button); 6827 6828 // check for IE < 9 mouseup outside of page boundary 6829 if (YAHOO.env.ua.ie && (YAHOO.env.ua.ie < 9) && !e.button) { 6830 YAHOO.log("button failure", "info", "DragDropMgr"); 6831 this.stopEvent(e); 6832 return this.handleMouseUp(e); 6833 } else { 6834 if (e.clientX < 0 || e.clientY < 0) { 6835 //This will stop the element from leaving the viewport in FF, Opera & Safari 6836 //Not turned on yet 6837 //YAHOO.log("Either clientX or clientY is negative, stop the event.", "info", "DragDropMgr"); 6838 //this.stopEvent(e); 6839 //return false; 6840 } 6841 } 6842 6843 if (!this.dragThreshMet) { 6844 var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e)); 6845 var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e)); 6846 // YAHOO.log("diffX: " + diffX + "diffY: " + diffY); 6847 if (diffX > this.clickPixelThresh || 6848 diffY > this.clickPixelThresh) { 6849 YAHOO.log("pixel threshold met", "info", "DragDropMgr"); 6850 this.startDrag(this.startX, this.startY); 6851 } 6852 } 6853 6854 if (this.dragThreshMet) { 6855 if (dc && dc.events.b4Drag) { 6856 dc.b4Drag(e); 6857 dc.fireEvent('b4DragEvent', { e: e}); 6858 } 6859 if (dc && dc.events.drag) { 6860 dc.onDrag(e); 6861 dc.fireEvent('dragEvent', { e: e}); 6862 } 6863 if (dc) { 6864 this.fireEvents(e, false); 6865 } 6866 } 6867 6868 this.stopEvent(e); 6869 } 6870 }, 6871 6872 /** 6873 * Iterates over all of the DragDrop elements to find ones we are 6874 * hovering over or dropping on 6875 * @method fireEvents 6876 * @param {Event} e the event 6877 * @param {boolean} isDrop is this a drop op or a mouseover op? 6878 * @private 6879 * @static 6880 */ 6881 fireEvents: function(e, isDrop) { 6882 var dc = this.dragCurrent; 6883 6884 // If the user did the mouse up outside of the window, we could 6885 // get here even though we have ended the drag. 6886 // If the config option dragOnly is true, bail out and don't fire the events 6887 if (!dc || dc.isLocked() || dc.dragOnly) { 6888 return; 6889 } 6890 6891 var x = YAHOO.util.Event.getPageX(e), 6892 y = YAHOO.util.Event.getPageY(e), 6893 pt = new YAHOO.util.Point(x,y), 6894 pos = dc.getTargetCoord(pt.x, pt.y), 6895 el = dc.getDragEl(), 6896 events = ['out', 'over', 'drop', 'enter'], 6897 curRegion = new YAHOO.util.Region( pos.y, 6898 pos.x + el.offsetWidth, 6899 pos.y + el.offsetHeight, 6900 pos.x ), 6901 6902 oldOvers = [], // cache the previous dragOver array 6903 inGroupsObj = {}, 6904 b4Results = {}, 6905 inGroups = [], 6906 data = { 6907 outEvts: [], 6908 overEvts: [], 6909 dropEvts: [], 6910 enterEvts: [] 6911 }; 6912 6913 6914 // Check to see if the object(s) we were hovering over is no longer 6915 // being hovered over so we can fire the onDragOut event 6916 for (var i in this.dragOvers) { 6917 6918 var ddo = this.dragOvers[i]; 6919 6920 if (! this.isTypeOfDD(ddo)) { 6921 continue; 6922 } 6923 if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) { 6924 data.outEvts.push( ddo ); 6925 } 6926 6927 oldOvers[i] = true; 6928 delete this.dragOvers[i]; 6929 } 6930 6931 for (var sGroup in dc.groups) { 6932 // YAHOO.log("Processing group " + sGroup); 6933 6934 if ("string" != typeof sGroup) { 6935 continue; 6936 } 6937 6938 for (i in this.ids[sGroup]) { 6939 var oDD = this.ids[sGroup][i]; 6940 if (! this.isTypeOfDD(oDD)) { 6941 continue; 6942 } 6943 6944 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) { 6945 if (this.isOverTarget(pt, oDD, this.mode, curRegion)) { 6946 inGroupsObj[sGroup] = true; 6947 // look for drop interactions 6948 if (isDrop) { 6949 data.dropEvts.push( oDD ); 6950 // look for drag enter and drag over interactions 6951 } else { 6952 6953 // initial drag over: dragEnter fires 6954 if (!oldOvers[oDD.id]) { 6955 data.enterEvts.push( oDD ); 6956 // subsequent drag overs: dragOver fires 6957 } else { 6958 data.overEvts.push( oDD ); 6959 } 6960 6961 this.dragOvers[oDD.id] = oDD; 6962 } 6963 } 6964 } 6965 } 6966 } 6967 6968 this.interactionInfo = { 6969 out: data.outEvts, 6970 enter: data.enterEvts, 6971 over: data.overEvts, 6972 drop: data.dropEvts, 6973 point: pt, 6974 draggedRegion: curRegion, 6975 sourceRegion: this.locationCache[dc.id], 6976 validDrop: isDrop 6977 }; 6978 6979 6980 for (var inG in inGroupsObj) { 6981 inGroups.push(inG); 6982 } 6983 6984 // notify about a drop that did not find a target 6985 if (isDrop && !data.dropEvts.length) { 6986 YAHOO.log(dc.id + " dropped, but not on a target", "info", "DragDropMgr"); 6987 this.interactionInfo.validDrop = false;
6988 if (dc.events.invalidDrop) { 6989 dc.onInvalidDrop(e); 6990 dc.fireEvent('invalidDropEvent', { e: e }); 6991 } 6992 } 6993 for (i = 0; i < events.length; i++) { 6994 var tmp = null; 6995 if (data[events[i] + 'Evts']) { 6996 tmp = data[events[i] + 'Evts']; 6997 } 6998 if (tmp && tmp.length) { 6999 var type = events[i].charAt(0).toUpperCase() + events[i].substr(1), 7000 ev = 'onDrag' + type, 7001 b4 = 'b4Drag' + type, 7002 cev = 'drag' + type + 'Event', 7003 check = 'drag' + type; 7004 if (this.mode) { 7005 YAHOO.log(dc.id + ' ' + ev + ': ' + tmp, "info", "DragDropMgr"); 7006 if (dc.events[b4]) { 7007 dc[b4](e, tmp, inGroups); 7008 b4Results[ev] = dc.fireEvent(b4 + 'Event', { event: e, info: tmp, group: inGroups }); 7009 7010 } 7011 if (dc.events[check] && (b4Results[ev] !== false)) { 7012 dc[ev](e, tmp, inGroups); 7013 dc.fireEvent(cev, { event: e, info: tmp, group: inGroups }); 7014 } 7015 } else { 7016 for (var b = 0, len = tmp.length; b < len; ++b) { 7017 YAHOO.log(dc.id + ' ' + ev + ': ' + tmp[b].id, "info", "DragDropMgr"); 7018 if (dc.events[b4]) { 7019 dc[b4](e, tmp[b].id, inGroups[0]); 7020 b4Results[ev] = dc.fireEvent(b4 + 'Event', { event: e, info: tmp[b].id, group: inGroups[0] }); 7021 } 7022 if (dc.events[check] && (b4Results[ev] !== false)) { 7023 dc[ev](e, tmp[b].id, inGroups[0]); 7024 dc.fireEvent(cev, { event: e, info: tmp[b].id, group: inGroups[0] }); 7025 } 7026 } 7027 } 7028 } 7029 } 7030 }, 7031 7032 /** 7033 * Helper function for getting the best match from the list of drag 7034 * and drop objects returned by the drag and drop events when we are 7035 * in INTERSECT mode. It returns either the first object that the 7036 * cursor is over, or the object that has the greatest overlap with 7037 * the dragged element. 7038 * @method getBestMatch 7039 * @param {DragDrop[]} dds The array of drag and drop objects 7040 * targeted 7041 * @return {DragDrop} The best single match 7042 * @static 7043 */ 7044 getBestMatch: function(dds) { 7045 var winner = null; 7046 7047 var len = dds.length; 7048 7049 if (len == 1) { 7050 winner = dds[0]; 7051 } else { 7052 // Loop through the targeted items 7053 for (var i=0; i<len; ++i) { 7054 var dd = dds[i]; 7055 // If the cursor is over the object, it wins. If the 7056 // cursor is over multiple matches, the first one we come 7057 // to wins. 7058 if (this.mode == this.INTERSECT && dd.cursorIsOver) { 7059 winner = dd; 7060 break; 7061 // Otherwise the object with the most overlap wins 7062 } else { 7063 if (!winner || !winner.overlap || (dd.overlap && 7064 winner.overlap.getArea() < dd.overlap.getArea())) { 7065 winner = dd; 7066 } 7067 } 7068 } 7069 } 7070 7071 return winner; 7072 }, 7073 7074 /** 7075 * Refreshes the cache of the top-left and bottom-right points of the 7076 * drag and drop objects in the specified group(s). This is in the 7077 * format that is stored in the drag and drop instance, so typical 7078 * usage is: 7079 * <code> 7080 * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups); 7081 * </code> 7082 * Alternatively: 7083 * <code> 7084 * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true}); 7085 * </code> 7086 * @TODO this really should be an indexed array. Alternatively this 7087 * method could accept both. 7088 * @method refreshCache 7089 * @param {Object} groups an associative array of groups to refresh 7090 * @static 7091 */ 7092 refreshCache: function(groups) { 7093 YAHOO.log("refreshing element location cache", "info", "DragDropMgr"); 7094 7095 // refresh everything if group array is not provided 7096 var g = groups || this.ids; 7097 7098 for (var sGroup in g) { 7099 if ("string" != typeof sGroup) { 7100 continue; 7101 } 7102 for (var i in this.ids[sGroup]) { 7103 var oDD = this.ids[sGroup][i]; 7104 7105 if (this.isTypeOfDD(oDD)) { 7106 var loc = this.getLocation(oDD); 7107 if (loc) { 7108 this.locationCache[oDD.id] = loc; 7109 } else { 7110 delete this.locationCache[oDD.id]; 7111YAHOO.log("Could not get the loc for " + oDD.id, "warn", "DragDropMgr"); 7112 } 7113 } 7114 } 7115 } 7116 }, 7117 7118 /** 7119 * This checks to make sure an element exists and is in the DOM. The 7120 * main purpose is to handle cases where innerHTML is used to remove 7121 * drag and drop objects from the DOM. IE provides an 'unspecified 7122 * error' when trying to access the offsetParent of such an element 7123 * @method verifyEl 7124 * @param {HTMLElement} el the element to check 7125 * @return {boolean} true if the element looks usable 7126 * @static 7127 */ 7128 verifyEl: function(el) { 7129 try { 7130 if (el) { 7131 var parent = el.offsetParent; 7132 if (parent) { 7133 return true; 7134 } 7135 } 7136 } catch(e) { 7137 YAHOO.log("detected problem with an element", "info", "DragDropMgr"); 7138 } 7139 7140 return false;
7141 }, 7142 7143 /** 7144 * Returns a Region object containing the drag and drop element's position 7145 * and size, including the padding configured for it 7146 * @method getLocation 7147 * @param {DragDrop} oDD the drag and drop object to get the 7148 * location for 7149 * @return {YAHOO.util.Region} a Region object representing the total area 7150 * the element occupies, including any padding 7151 * the instance is configured for. 7152 * @static 7153 */ 7154 getLocation: function(oDD) { 7155 if (! this.isTypeOfDD(oDD)) { 7156 YAHOO.log(oDD + " is not a DD obj", "info", "DragDropMgr"); 7157 return null; 7158 } 7159 7160 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l; 7161 7162 try { 7163 pos= YAHOO.util.Dom.getXY(el); 7164 } catch (e) { } 7165 7166 if (!pos) { 7167 YAHOO.log("getXY failed", "info", "DragDropMgr"); 7168 return null; 7169 } 7170 7171 x1 = pos[0]; 7172 x2 = x1 + el.offsetWidth; 7173 y1 = pos[1]; 7174 y2 = y1 + el.offsetHeight; 7175 7176 t = y1 - oDD.padding[0]; 7177 r = x2 + oDD.padding[1]; 7178 b = y2 + oDD.padding[2]; 7179 l = x1 - oDD.padding[3]; 7180 7181 return new YAHOO.util.Region( t, r, b, l ); 7182 }, 7183 7184 /** 7185 * Checks the cursor location to see if it over the target 7186 * @method isOverTarget 7187 * @param {YAHOO.util.Point} pt The point to evaluate 7188 * @param {DragDrop} oTarget the DragDrop object we are inspecting 7189 * @param {boolean} intersect true if we are in intersect mode 7190 * @param {YAHOO.util.Region} pre-cached location of the dragged element 7191 * @return {boolean} true if the mouse is over the target 7192 * @private 7193 * @static 7194 */ 7195 isOverTarget: function(pt, oTarget, intersect, curRegion) { 7196 // use cache if available 7197 var loc = this.locationCache[oTarget.id]; 7198 if (!loc || !this.useCache) { 7199 YAHOO.log("cache not populated", "info", "DragDropMgr"); 7200 loc = this.getLocation(oTarget); 7201 this.locationCache[oTarget.id] = loc; 7202 7203 YAHOO.log("cache: " + loc, "info", "DragDropMgr"); 7204 } 7205 7206 if (!loc) { 7207 YAHOO.log("could not get the location of the element", "info", "DragDropMgr"); 7208 return false; 7209 } 7210 7211 //YAHOO.log("loc: " + loc + ", pt: " + pt); 7212 oTarget.cursorIsOver = loc.contains( pt ); 7213 7214 // DragDrop is using this as a sanity check for the initial mousedown 7215 // in this case we are done. In POINT mode, if the drag obj has no 7216 // contraints, we are done. Otherwise we need to evaluate the 7217 // region the target as occupies to determine if the dragged element 7218 // overlaps with it. 7219 7220 var dc = this.dragCurrent; 7221 if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) { 7222 7223 //if (oTarget.cursorIsOver) { 7224 //YAHOO.log("over " + oTarget + ", " + loc + ", " + pt, "warn"); 7225 //} 7226 return oTarget.cursorIsOver; 7227 } 7228 7229 oTarget.overlap = null; 7230 7231 7232 // Get the current location of the drag element, this is the 7233 // location of the mouse event less the delta that represents 7234 // where the original mousedown happened on the element. We 7235 // need to consider constraints and ticks as well. 7236 7237 if (!curRegion) { 7238 var pos = dc.getTargetCoord(pt.x, pt.y); 7239 var el = dc.getDragEl(); 7240 curRegion = new YAHOO.util.Region( pos.y, 7241 pos.x + el.offsetWidth, 7242 pos.y + el.offsetHeight, 7243 pos.x ); 7244 } 7245 7246 var overlap = curRegion.intersect(loc); 7247 7248 if (overlap) { 7249 oTarget.overlap = overlap; 7250 return (intersect) ? true : oTarget.cursorIsOver; 7251 } else { 7252 return false;
7253 } 7254 }, 7255 7256 /** 7257 * unload event handler 7258 * @method _onUnload 7259 * @private 7260 * @static 7261 */ 7262 _onUnload: function(e, me) { 7263 this.unregAll(); 7264 }, 7265 7266 /** 7267 * Cleans up the drag and drop events and objects. 7268 * @method unregAll 7269 * @private 7270 * @static 7271 */ 7272 unregAll: function() { 7273 YAHOO.log("unregister all", "info", "DragDropMgr"); 7274 7275 if (this.dragCurrent) { 7276 this.stopDrag(); 7277 this.dragCurrent = null; 7278 } 7279 7280 this._execOnAll("unreg", []); 7281 7282 //for (var i in this.elementCache) { 7283 //delete this.elementCache[i]; 7284 //} 7285 //this.elementCache = {}; 7286 7287 this.ids = {}; 7288 }, 7289 7290 /** 7291 * A cache of DOM elements 7292 * @property elementCache 7293 * @private 7294 * @static 7295 * @deprecated elements are not cached now 7296 */ 7297 elementCache: {}, 7298 7299 /** 7300 * Get the wrapper for the DOM element specified 7301 * @method getElWrapper 7302 * @param {String} id the id of the element to get 7303 * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element 7304 * @private 7305 * @deprecated This wrapper isn't that useful 7306 * @static 7307 */ 7308 getElWrapper: function(id) { 7309 var oWrapper = this.elementCache[id]; 7310 if (!oWrapper || !oWrapper.el) { 7311 oWrapper = this.elementCache[id] = 7312 new this.ElementWrapper(YAHOO.util.Dom.get(id)); 7313 } 7314 return oWrapper; 7315 }, 7316 7317 /** 7318 * Returns the actual DOM element 7319 * @method getElement 7320 * @param {String} id the id of the elment to get 7321 * @return {Object} The element 7322 * @deprecated use YAHOO.util.Dom.get instead 7323 * @static 7324 */ 7325 getElement: function(id) { 7326 return YAHOO.util.Dom.get(id); 7327 }, 7328 7329 /** 7330 * Returns the style property for the DOM element (i.e., 7331 * document.getElById(id).style) 7332 * @method getCss 7333 * @param {String} id the id of the elment to get 7334 * @return {Object} The style property of the element 7335 * @deprecated use YAHOO.util.Dom instead 7336 * @static 7337 */ 7338 getCss: function(id) { 7339 var el = YAHOO.util.Dom.get(id); 7340 return (el) ? el.style : null; 7341 }, 7342 7343 /** 7344 * Inner class for cached elements 7345 * @class DragDropMgr.ElementWrapper 7346 * @for DragDropMgr 7347 * @private 7348 * @deprecated 7349 */ 7350 ElementWrapper: function(el) { 7351 /** 7352 * The element 7353 * @property el 7354 */ 7355 this.el = el || null; 7356 /** 7357 * The element id 7358 * @property id 7359 */ 7360 this.id = this.el && el.id; 7361 /** 7362 * A reference to the style property 7363 * @property css 7364 */ 7365 this.css = this.el && el.style; 7366 }, 7367 7368 /** 7369 * Returns the X position of an html element 7370 * @method getPosX 7371 * @param el the element for which to get the position 7372 * @return {int} the X coordinate 7373 * @for DragDropMgr 7374 * @deprecated use YAHOO.util.Dom.getX instead 7375 * @static 7376 */ 7377 getPosX: function(el) { 7378 return YAHOO.util.Dom.getX(el); 7379 }, 7380 7381 /** 7382 * Returns the Y position of an html element 7383 * @method getPosY 7384 * @param el the element for which to get the position 7385 * @return {int} the Y coordinate 7386 * @deprecated use YAHOO.util.Dom.getY instead 7387 * @static 7388 */ 7389 getPosY: function(el) { 7390 return YAHOO.util.Dom.getY(el); 7391 }, 7392 7393 /** 7394 * Swap two nodes. In IE, we use the native method, for others we 7395 * emulate the IE behavior 7396 * @method swapNode 7397 * @param n1 the first node to swap 7398 * @param n2 the other node to swap 7399 * @static 7400 */ 7401 swapNode: function(n1, n2) { 7402 if (n1.swapNode) { 7403 n1.swapNode(n2); 7404 } else { 7405 var p = n2.parentNode; 7406 var s = n2.nextSibling; 7407 7408 if (s == n1) { 7409 p.insertBefore(n1, n2); 7410 } else if (n2 == n1.nextSibling) { 7411 p.insertBefore(n2, n1); 7412 } else { 7413 n1.parentNode.replaceChild(n2, n1); 7414 p.insertBefore(n1, s); 7415 } 7416 } 7417 }, 7418 7419 /** 7420 * Returns the current scroll position 7421 * @method getScroll 7422 * @private 7423 * @static 7424 */ 7425 getScroll: function () { 7426 var t, l, dde=document.documentElement, db=document.body; 7427 if (dde && (dde.scrollTop || dde.scrollLeft)) { 7428 t = dde.scrollTop; 7429 l = dde.scrollLeft; 7430 } else if (db) { 7431 t = db.scrollTop; 7432 l = db.scrollLeft; 7433 } else { 7434 YAHOO.log("could not get scroll property", "info", "DragDropMgr"); 7435 } 7436 return { top: t, left: l }; 7437 }, 7438 7439 /** 7440 * Returns the specified element style property 7441 * @method getStyle 7442 * @param {HTMLElement} el the element 7443 * @param {string} styleProp the style property 7444 * @return {string} The value of the style property 7445 * @deprecated use YAHOO.util.Dom.getStyle 7446 * @static 7447 */ 7448 getStyle: function(el, styleProp) { 7449 return YAHOO.util.Dom.getStyle(el, styleProp); 7450 }, 7451 7452 /** 7453 * Gets the scrollTop 7454 * @method getScrollTop 7455 * @return {int} the document's scrollTop 7456 * @static 7457 */
7458 getScrollTop: function () { return this.getScroll().top; }, 7459 7460 /** 7461 * Gets the scrollLeft 7462 * @method getScrollLeft 7463 * @return {int} the document's scrollTop 7464 * @static 7465 */ 7466 getScrollLeft: function () { return this.getScroll().left; }, 7467 7468 /** 7469 * Sets the x/y position of an element to the location of the 7470 * target element. 7471 * @method moveToEl 7472 * @param {HTMLElement} moveEl The element to move 7473 * @param {HTMLElement} targetEl The position reference element 7474 * @static 7475 */ 7476 moveToEl: function (moveEl, targetEl) { 7477 var aCoord = YAHOO.util.Dom.getXY(targetEl); 7478 YAHOO.log("moveToEl: " + aCoord, "info", "DragDropMgr"); 7479 YAHOO.util.Dom.setXY(moveEl, aCoord); 7480 }, 7481 7482 /** 7483 * Gets the client height 7484 * @method getClientHeight 7485 * @return {int} client height in px 7486 * @deprecated use YAHOO.util.Dom.getViewportHeight instead 7487 * @static 7488 */ 7489 getClientHeight: function() { 7490 return YAHOO.util.Dom.getViewportHeight(); 7491 }, 7492 7493 /** 7494 * Gets the client width 7495 * @method getClientWidth 7496 * @return {int} client width in px 7497 * @deprecated use YAHOO.util.Dom.getViewportWidth instead 7498 * @static 7499 */ 7500 getClientWidth: function() { 7501 return YAHOO.util.Dom.getViewportWidth(); 7502 }, 7503 7504 /** 7505 * Numeric array sort function 7506 * @method numericSort 7507 * @static 7508 */ 7509 numericSort: function(a, b) { return (a - b); }, 7510 7511 /** 7512 * Internal counter 7513 * @property _timeoutCount 7514 * @private 7515 * @static 7516 */ 7517 _timeoutCount: 0, 7518 7519 /** 7520 * Trying to make the load order less important. Without this we get 7521 * an error if this file is loaded before the Event Utility. 7522 * @method _addListeners 7523 * @private 7524 * @static 7525 */ 7526 _addListeners: function() { 7527 var DDM = YAHOO.util.DDM; 7528 if ( YAHOO.util.Event && document ) { 7529 DDM._onLoad(); 7530 } else { 7531 if (DDM._timeoutCount > 2000) { 7532 YAHOO.log("DragDrop requires the Event Utility", "error", "DragDropMgr"); 7533 } else { 7534 setTimeout(DDM._addListeners, 10); 7535 if (document && document.body) { 7536 DDM._timeoutCount += 1; 7537 } 7538 } 7539 } 7540 }, 7541 7542 /** 7543 * Recursively searches the immediate parent and all child nodes for 7544 * the handle element in order to determine wheter or not it was 7545 * clicked. 7546 * @method handleWasClicked 7547 * @param node the html element to inspect 7548 * @static 7549 */ 7550 handleWasClicked: function(node, id) { 7551 if (this.isHandle(id, node.id)) { 7552 YAHOO.log("clicked node is a handle", "info", "DragDropMgr"); 7553 return true; 7554 } else { 7555 // check to see if this is a text node child of the one we want 7556 var p = node.parentNode; 7557 // YAHOO.log("p: " + p); 7558 7559 while (p) { 7560 if (this.isHandle(id, p.id)) { 7561 return true; 7562 } else { 7563 YAHOO.log(p.id + " is not a handle", "info", "DragDropMgr"); 7564 p = p.parentNode; 7565 } 7566 } 7567 } 7568 7569 return false; 7570 } 7571 7572 }; 7573 7574}(); 7575 7576// shorter alias, save a few bytes 7577YAHOO.util.DDM = YAHOO.util.DragDropMgr; 7578YAHOO.util.DDM._addListeners(); 7579 7580} 7581 7582(function() { 7583 7584var Event=YAHOO.util.Event; 7585var Dom=YAHOO.util.Dom; 7586 7587/** 7588 * Defines the interface and base operation of items that that can be 7589 * dragged or can be drop targets. It was designed to be extended, overriding 7590 * the event handlers for startDrag, onDrag, onDragOver, onDragOut. 7591 * Up to three html elements can be associated with a DragDrop instance: 7592 * <ul> 7593 * <li>linked element: the element that is passed into the constructor. 7594 * This is the element which defines the boundaries for interaction with 7595 * other DragDrop objects.</li> 7596 * <li>
7596handle element(s): The drag operation only occurs if the element that 7597 * was clicked matches a handle element. By default this is the linked 7598 * element, but there are times that you will want only a portion of the 7599 * linked element to initiate the drag operation, and the setHandleElId() 7600 * method provides a way to define this.</li> 7601 * <li>drag element: this represents an the element that would be moved along 7602 * with the cursor during a drag operation. By default, this is the linked 7603 * element itself as in {@link YAHOO.util.DD}. setDragElId() lets you define 7604 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy} 7605 * </li> 7606 * </ul> 7607 * This class should not be instantiated until the onload event to ensure that 7608 * the associated elements are available. 7609 * The following would define a DragDrop obj that would interact with any 7610 * other DragDrop obj in the "group1" group: 7611 * <pre> 7612 * dd = new YAHOO.util.DragDrop("div1", "group1"); 7613 * </pre> 7614 * Since none of the event handlers have been implemented, nothing would 7615 * actually happen if you were to run the code above. Normally you would 7616 * override this class or one of the default implementations, but you can 7617 * also override the methods you want on an instance of the class... 7618 * <pre> 7619 * dd.onDragDrop = function(e, id) { 7620 * alert("dd was dropped on " + id); 7621 * } 7622 * </pre> 7623 * @namespace YAHOO.util 7624 * @class DragDrop 7625 * @constructor 7626 * @param {String} id of the element that is linked to this instance 7627 * @param {String} sGroup the group of related DragDrop objects 7628 * @param {object} config an object containing configurable attributes 7629 * Valid properties for DragDrop: 7630 * padding, isTarget, maintainOffset, primaryButtonOnly, 7631 */ 7632YAHOO.util.DragDrop = function(id, sGroup, config) { 7633 if (id) { 7634 this.init(id, sGroup, config); 7635 } 7636}; 7637 7638YAHOO.util.DragDrop.prototype = { 7639 /** 7640 * An Object Literal containing the events that we will be using: mouseDown, b4MouseDown, mouseUp, b4StartDrag, startDrag, b4EndDrag, endDrag, mouseUp, drag, b4Drag, invalidDrop, b4DragOut, dragOut, dragEnter, b4DragOver, dragOver, b4DragDrop, dragDrop 7641 * By setting any of these to false, then event will not be fired. 7642 * @property events 7643 * @type object 7644 */ 7645 events: null, 7646 /** 7647 * @method on 7648 * @description Shortcut for EventProvider.subscribe, see <a href="YAHOO.util.EventProvider.html#subscribe">YAHOO.util.EventProvider.subscribe</a> 7649 */ 7650 on: function() { 7651 this.subscribe.apply(this, arguments); 7652 }, 7653 /** 7654 * The id of the element associated with this object. This is what we 7655 * refer to as the "linked element" because the size and position of 7656 * this element is used to determine when the drag and drop objects have 7657 * interacted. 7658 * @property id
7659 * @type String 7660 */ 7661 id: null, 7662 7663 /** 7664 * Configuration attributes passed into the constructor 7665 * @property config 7666 * @type object 7667 */ 7668 config: null, 7669 7670 /** 7671 * The id of the element that will be dragged. By default this is same 7672 * as the linked element , but could be changed to another element. Ex: 7673 * YAHOO.util.DDProxy 7674 * @property dragElId 7675 * @type String 7676 * @private 7677 */ 7678 dragElId: null, 7679 7680 /** 7681 * the id of the element that initiates the drag operation. By default 7682 * this is the linked element, but could be changed to be a child of this 7683 * element. This lets us do things like only starting the drag when the 7684 * header element within the linked html element is clicked. 7685 * @property handleElId 7686 * @type String 7687 * @private 7688 */ 7689 handleElId: null, 7690 7691 /** 7692 * An associative array of HTML tags that will be ignored if clicked. 7693 * @property invalidHandleTypes 7694 * @type {string: string} 7695 */ 7696 invalidHandleTypes: null, 7697 7698 /** 7699 * An associative array of ids for elements that will be ignored if clicked 7700 * @property invalidHandleIds 7701 * @type {string: string} 7702 */ 7703 invalidHandleIds: null, 7704 7705 /** 7706 * An indexted array of css class names for elements that will be ignored 7707 * if clicked. 7708 * @property invalidHandleClasses 7709 * @type string[] 7710 */ 7711 invalidHandleClasses: null, 7712 7713 /** 7714 * The linked element's absolute X position at the time the drag was 7715 * started 7716 * @property startPageX 7717 * @type int 7718 * @private 7719 */ 7720 startPageX: 0, 7721 7722 /** 7723 * The linked element's absolute X position at the time the drag was 7724 * started 7725 * @property startPageY 7726 * @type int 7727 * @private 7728 */ 7729 startPageY: 0, 7730 7731 /** 7732 * The group defines a logical collection of DragDrop objects that are 7733 * related. Instances only get events when interacting with other 7734 * DragDrop object in the same group. This lets us define multiple 7735 * groups using a single DragDrop subclass if we want. 7736 * @property groups 7737 * @type {string: string} 7738 */ 7739 groups: null, 7740 7741 /** 7742 * Individual drag/drop instances can be locked. This will prevent 7743 * onmousedown start drag. 7744 * @property locked 7745 * @type boolean 7746 * @private 7747 */ 7748 locked: false, 7749 7750 /** 7751 * Lock this instance 7752 * @method lock 7753 */ 7754 lock: function() { this.locked = true; }, 7755 7756 /** 7757 * Unlock this instace 7758 * @method unlock 7759 */ 7760 unlock: function() { this.locked = false; }, 7761 7762 /** 7763 * By default, all instances can be a drop target. This can be disabled by 7764 * setting isTarget to false. 7765 * @property isTarget 7766 * @type boolean 7767 */ 7768 isTarget: true, 7769 7770 /** 7771 * The padding configured for this drag and drop object for calculating 7772 * the drop zone intersection with this object. 7773 * @property padding 7774 * @type int[] 7775 */ 7776 padding: null, 7777 /** 7778 * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping) 7779 * @property dragOnly 7780 * @type Boolean 7781 */ 7782 dragOnly: false, 7783 7784 /** 7785 * If this flag is true, a shim will be placed over the screen/viewable area to track mouse events. Should help with dragging elements over iframes and other controls. 7786 * @property useShim 7787 * @type Boolean 7788 */ 7789 useShim: false, 7790 7791 /** 7792 * Cached reference to the linked element 7793 * @property _domRef 7794 * @private 7795 */ 7796 _domRef: null, 7797 7798 /** 7799 * Internal typeof flag 7800 * @property __ygDragDrop 7801 * @private 7802 */ 7803 __ygDragDrop: true, 7804 7805 /** 7806 * Set to true when horizontal contraints are applied 7807 * @property constrainX 7808 * @type boolean 7809 * @private 7810 */ 7811 constrainX: false, 7812 7813 /** 7814 * Set to true when vertical contraints are applied 7815 * @property constrainY 7816 * @type boolean 7817 * @private 7818 */ 7819 constrainY: false, 7820 7821 /** 7822 * The left constraint 7823 * @property minX 7824 * @type int 7825 * @private 7826 */ 7827 minX: 0, 7828 7829 /**
7830 * The right constraint 7831 * @property maxX 7832 * @type int 7833 * @private 7834 */ 7835 maxX: 0, 7836 7837 /** 7838 * The up constraint 7839 * @property minY 7840 * @type int 7841 * @type int 7842 * @private 7843 */ 7844 minY: 0, 7845 7846 /** 7847 * The down constraint 7848 * @property maxY 7849 * @type int 7850 * @private 7851 */ 7852 maxY: 0, 7853 7854 /** 7855 * The difference between the click position and the source element's location 7856 * @property deltaX 7857 * @type int 7858 * @private 7859 */ 7860 deltaX: 0, 7861 7862 /** 7863 * The difference between the click position and the source element's location 7864 * @property deltaY 7865 * @type int 7866 * @private 7867 */ 7868 deltaY: 0, 7869 7870 /** 7871 * Maintain offsets when we resetconstraints. Set to true when you want 7872 * the position of the element relative to its parent to stay the same 7873 * when the page changes 7874 * 7875 * @property maintainOffset 7876 * @type boolean 7877 */ 7878 maintainOffset: false, 7879 7880 /** 7881 * Array of pixel locations the element will snap to if we specified a 7882 * horizontal graduation/interval. This array is generated automatically 7883 * when you define a tick interval. 7884 * @property xTicks 7885 * @type int[] 7886 */ 7887 xTicks: null, 7888 7889 /** 7890 * Array of pixel locations the element will snap to if we specified a 7891 * vertical graduation/interval. This array is generated automatically 7892 * when you define a tick interval. 7893 * @property yTicks 7894 * @type int[] 7895 */ 7896 yTicks: null, 7897 7898 /** 7899 * By default the drag and drop instance will only respond to the primary 7900 * button click (left button for a right-handed mouse). Set to true to 7901 * allow drag and drop to start with any mouse click that is propogated 7902 * by the browser 7903 * @property primaryButtonOnly 7904 * @type boolean 7905 */ 7906 primaryButtonOnly: true, 7907 7908 /** 7909 * The availabe property is false until the linked dom element is accessible. 7910 * @property available 7911 * @type boolean 7912 */ 7913 available: false, 7914 7915 /** 7916 * By default, drags can only be initiated if the mousedown occurs in the 7917 * region the linked element is. This is done in part to work around a 7918 * bug in some browsers that mis-report the mousedown if the previous 7919 * mouseup happened outside of the window. This property is set to true 7920 * if outer handles are defined. 7921 * 7922 * @property hasOuterHandles 7923 * @type boolean 7924 * @default false 7925 */ 7926 hasOuterHandles: false, 7927 7928 /** 7929 * Property that is assigned to a drag and drop object when testing to 7930 * see if it is being targeted by another dd object. This property 7931 * can be used in intersect mode to help determine the focus of 7932 * the mouse interaction. DDM.getBestMatch uses this property first to 7933 * determine the closest match in INTERSECT mode when multiple targets 7934 * are part of the same interaction. 7935 * @property cursorIsOver 7936 * @type boolean 7937 */ 7938 cursorIsOver: false, 7939 7940 /** 7941 * Property that is assigned to a drag and drop object when testing to 7942 * see if it is being targeted by another dd object. This is a region 7943 * that represents the area the draggable element overlaps this target. 7944 * DDM.getBestMatch uses this property to compare the size of the overlap 7945 * to that of other targets in order to determine the closest match in 7946 * INTERSECT mode when multiple targets are part of the same interaction. 7947 * @property overlap 7948 * @type YAHOO.util.Region 7949 */ 7950 overlap: null, 7951 7952 /** 7953 * Code that executes immediately before the startDrag event 7954 * @method b4StartDrag 7955 * @private 7956 */ 7957 b4StartDrag: function(x, y) { }, 7958 7959 /** 7960 * Abstract method called after a drag/drop object is clicked 7961 * and the drag or mousedown time thresholds have beeen met. 7962 * @method startDrag 7963 * @param {int} X click location 7964 * @param {int} Y click location 7965 */ 7966 startDrag: function(x, y) { /* override this */ }, 7967 7968 /** 7969 * Code that executes immediately before the onDrag event 7970 * @method b4Drag 7971 * @private 7972 */ 7973 b4Drag: function(e) { }, 7974 7975 /** 7976 * Abstract method called during the onMouseMove event while dragging an 7977 * object. 7978 * @method onDrag 7979 * @param {Event} e the mousemove event 7980 */ 7981 onDrag: function(e) { /* override this */ }, 7982 7983 /** 7984 * Abstract method called when this element fist begins hovering over 7985 * another DragDrop obj 7986 * @method onDragEnter 7987 * @param {Event} e the mousemove event 7988 * @param {String|DragDrop[]} id In POINT mode, the element 7989 * id this is hovering over. In INTERSECT mode, an array of one or more 7990 * dragdrop items being hovered over. 7991 */ 7992 onDragEnter: function(e, id) { /* override this */ }, 7993 7994 /** 7995 * Code that executes immediately before the onDragOver event 7996 * @method b4DragOver 7997 * @private 7998 */ 7999 b4DragOver: function(e) { }, 8000 8001 /** 8002 * Abstract method called when this element is hovering over another 8003 * DragDrop obj 8004 * @method onDragOver 8005 * @param {Event} e the mousemove event 8006 * @param {String|DragDrop[]} id In POINT mode, the element 8007 * id this is hovering over. In INTERSECT mode, an array of dd items 8008 * being hovered over. 8009 */ 8010 onDragOver: function(e, id) { /* override this */ }, 8011 8012 /** 8013 * Code that executes immediately before the onDragOut event 8014 * @method b4DragOut 8015 * @private 8016 */ 8017 b4DragOut: function(e) { }, 8018 8019 /** 8020 * Abstract method called when we are no longer hovering over an element 8021 * @method onDragOut 8022 * @param {Event} e the mousemove event 8023 * @param {String|DragDrop[]} id In POINT mode, the element 8024 * id this was hovering over. In INTERSECT mode, an array of dd items 8025 * that the mouse is no longer over. 8026 */ 8027 onDragOut: function(e, id) { /* override this */ }, 8028 8029 /** 8030 * Code that executes immediately before the onDragDrop event 8031 * @method b4DragDrop 8032 * @private 8033 */ 8034 b4DragDrop: function(e) { }, 8035 8036 /** 8037 * Abstract method called when this item is dropped on another DragDrop 8038 * obj 8039 * @method onDragDrop 8040 * @param {Event} e the mouseup event 8041 * @param {String|DragDrop[]} id In POINT mode, the element 8042 * id this was dropped on. In INTERSECT mode, an array of dd items this 8043 * was dropped on. 8044 */ 8045 onDragDrop: function(e, id) { /* override this */ }, 8046 8047 /** 8048 * Abstract method called when this item is dropped on an area with no 8049 * drop target 8050 * @method onInvalidDrop 8051 * @param {Event} e the mouseup event 8052 */ 8053 onInvalidDrop: function(e) { /* override this */ }, 8054 8055 /** 8056 * Code that executes immediately before the endDrag event 8057 * @method b4EndDrag 8058 * @private 8059 */ 8060 b4EndDrag: function(e) { }, 8061 8062 /** 8063 * Fired when we are done dragging the object 8064 * @method endDrag 8065 * @param {Event} e the mouseup event 8066 */ 8067 endDrag: function(e) { /* override this */ }, 8068 8069 /** 8070 * Code executed immediately before the onMouseDown event 8071 * @method b4MouseDown 8072 * @param {Event} e the mousedown event 8073 * @private 8074 */ 8075 b4MouseDown: function(e) { }, 8076 8077 /** 8078 * Event handler that fires when a drag/drop obj gets a mousedown 8079 * @method onMouseDown 8080 * @param {Event} e the mousedown event 8081 */ 8082 onMouseDown: function(e) { /* override this */ }, 8083 8084 /** 8085 * Event handler that fires when a drag/drop obj gets a mouseup 8086 * @method onMouseUp 8087 * @param {Event} e the mouseup event 8088 */ 8089 onMouseUp: function(e) { /* override this */ }, 8090 8091 /** 8092 * Override the onAvailable method to do what is needed after the initial 8093 * position was determined. 8094 * @method onAvailable 8095 */ 8096 onAvailable: function () { 8097 //this.logger.log("onAvailable (base)"); 8098 }, 8099 8100 /** 8101 * Returns a reference to the linked element 8102 * @method getEl 8103 * @return {HTMLElement} the html element 8104 */ 8105 getEl: function() { 8106 if (!this._domRef) {
8107 this._domRef = Dom.get(this.id); 8108 } 8109 8110 return this._domRef; 8111 }, 8112 8113 /** 8114 * Returns a reference to the actual element to drag. By default this is 8115 * the same as the html element, but it can be assigned to another 8116 * element. An example of this can be found in YAHOO.util.DDProxy 8117 * @method getDragEl 8118 * @return {HTMLElement} the html element 8119 */ 8120 getDragEl: function() { 8121 return Dom.get(this.dragElId); 8122 }, 8123 8124 /** 8125 * Sets up the DragDrop object. Must be called in the constructor of any 8126 * YAHOO.util.DragDrop subclass 8127 * @method init 8128 * @param id the id of the linked element 8129 * @param {String} sGroup the group of related items 8130 * @param {object} config configuration attributes 8131 */ 8132 init: function(id, sGroup, config) { 8133 this.initTarget(id, sGroup, config); 8134 Event.on(this._domRef || this.id, "mousedown", 8135 this.handleMouseDown, this, true); 8136 8137 // Event.on(this.id, "selectstart", Event.preventDefault); 8138 for (var i in this.events) { 8139 this.createEvent(i + 'Event'); 8140 } 8141 8142 }, 8143 8144 /** 8145 * Initializes Targeting functionality only... the object does not 8146 * get a mousedown handler. 8147 * @method initTarget 8148 * @param id the id of the linked element 8149 * @param {String} sGroup the group of related items 8150 * @param {object} config configuration attributes 8151 */ 8152 initTarget: function(id, sGroup, config) { 8153 8154 // configuration attributes 8155 this.config = config || {}; 8156 8157 this.events = {}; 8158 8159 // create a local reference to the drag and drop manager 8160 this.DDM = YAHOO.util.DDM; 8161 8162 // initialize the groups object 8163 this.groups = {}; 8164 8165 // assume that we have an element reference instead of an id if the 8166 // parameter is not a string 8167 if (typeof id !== "string") { 8168 YAHOO.log("id is not a string, assuming it is an HTMLElement"); 8169 this._domRef = id; 8170 id = Dom.generateId(id); 8171 } 8172 8173 // set the id 8174 this.id = id; 8175 8176 // add to an interaction group 8177 this.addToGroup((sGroup) ? sGroup : "default"); 8178 8179 // We don't want to register this as the handle with the manager 8180 // so we just set the id rather than calling the setter. 8181 this.handleElId = id; 8182 8183 Event.onAvailable(id, this.handleOnAvailable, this, true); 8184 8185 // create a logger instance 8186 this.logger = (YAHOO.widget.LogWriter) ? 8187 new YAHOO.widget.LogWriter(this.toString()) : YAHOO; 8188 8189 // the linked element is the element that gets dragged by default 8190 this.setDragElId(id); 8191 8192 // by default, clicked anchors will not start drag operations. 8193 // @TODO what else should be here? Probably form fields. 8194 this.invalidHandleTypes = { A: "A" }; 8195 this.invalidHandleIds = {}; 8196 this.invalidHandleClasses = []; 8197 8198 this.applyConfig(); 8199 }, 8200 8201 /** 8202 * Applies the configuration parameters that were passed into the constructor. 8203 * This is supposed to happen at each level through the inheritance chain. So 8204 * a DDProxy implentation will execute apply config on DDProxy, DD, and 8205 * DragDrop in order to get all of the parameters that are available in 8206 * each object. 8207 * @method applyConfig 8208 */ 8209 applyConfig: function() { 8210 this.events = { 8211 mouseDown: true, 8212 b4MouseDown: true, 8213 mouseUp: true, 8214 b4StartDrag: true, 8215 startDrag: true, 8216 b4EndDrag: true, 8217 endDrag: true, 8218 drag: true, 8219 b4Drag: true, 8220 invalidDrop: true, 8221 b4DragOut: true, 8222 dragOut: true, 8223 dragEnter: true, 8224 b4DragOver: true, 8225 dragOver: true, 8226 b4DragDrop: true, 8227 dragDrop: true 8228 }; 8229 8230 if (this.config.events) { 8231 for (var i in this.config.events) { 8232 if (this.config.events[i] === false) { 8233 this.events[i] = false;
8234 } 8235 } 8236 } 8237 8238 8239 // configurable properties: 8240 // padding, isTarget, maintainOffset, primaryButtonOnly 8241 this.padding = this.config.padding || [0, 0, 0, 0]; 8242 this.isTarget = (this.config.isTarget !== false); 8243 this.maintainOffset = (this.config.maintainOffset); 8244 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); 8245 this.dragOnly = ((this.config.dragOnly === true) ? true : false); 8246 this.useShim = ((this.config.useShim === true) ? true : false); 8247 }, 8248 8249 /** 8250 * Executed when the linked element is available 8251 * @method handleOnAvailable 8252 * @private 8253 */ 8254 handleOnAvailable: function() { 8255 //this.logger.log("handleOnAvailable"); 8256 this.available = true; 8257 this.resetConstraints(); 8258 this.onAvailable(); 8259 }, 8260 8261 /** 8262 * Configures the padding for the target zone in px. Effectively expands 8263 * (or reduces) the virtual object size for targeting calculations. 8264 * Supports css-style shorthand; if only one parameter is passed, all sides 8265 * will have that padding, and if only two are passed, the top and bottom 8266 * will have the first param, the left and right the second. 8267 * @method setPadding 8268 * @param {int} iTop Top pad 8269 * @param {int} iRight Right pad 8270 * @param {int} iBot Bot pad 8271 * @param {int} iLeft Left pad 8272 */ 8273 setPadding: function(iTop, iRight, iBot, iLeft) { 8274 // this.padding = [iLeft, iRight, iTop, iBot]; 8275 if (!iRight && 0 !== iRight) { 8276 this.padding = [iTop, iTop, iTop, iTop]; 8277 } else if (!iBot && 0 !== iBot) { 8278 this.padding = [iTop, iRight, iTop, iRight]; 8279 } else { 8280 this.padding = [iTop, iRight, iBot, iLeft]; 8281 } 8282 }, 8283 8284 /** 8285 * Stores the initial placement of the linked element. 8286 * @method setInitialPosition 8287 * @param {int} diffX the X offset, default 0 8288 * @param {int} diffY the Y offset, default 0 8289 * @private 8290 */ 8291 setInitPosition: function(diffX, diffY) { 8292 var el = this.getEl(); 8293 8294 if (!this.DDM.verifyEl(el)) { 8295 if (el && el.style && (el.style.display == 'none')) { 8296 this.logger.log(this.id + " can not get initial position, element style is display: none"); 8297 } else { 8298 this.logger.log(this.id + " element is broken"); 8299 } 8300 return; 8301 } 8302 8303 var dx = diffX || 0; 8304 var dy = diffY || 0; 8305 8306 var p = Dom.getXY( el ); 8307 8308 this.initPageX = p[0] - dx; 8309 this.initPageY = p[1] - dy; 8310 8311 this.lastPageX = p[0]; 8312 this.lastPageY = p[1]; 8313 8314 this.logger.log(this.id + " initial position: " + this.initPageX + 8315 ", " + this.initPageY); 8316 8317 8318 this.setStartPosition(p); 8319 }, 8320 8321 /** 8322 * Sets the start position of the element. This is set when the obj 8323 * is initialized, the reset when a drag is started. 8324 * @method setStartPosition 8325 * @param pos current position (from previous lookup) 8326 * @private 8327 */ 8328 setStartPosition: function(pos) { 8329 var p = pos || Dom.getXY(this.getEl()); 8330 8331 this.deltaSetXY = null; 8332 8333 this.startPageX = p[0]; 8334 this.startPageY = p[1]; 8335 }, 8336 8337 /** 8338 * Add this instance to a group of related drag/drop objects. All 8339 * instances belong to at least one group, and can belong to as many 8340 * groups as needed. 8341 * @method addToGroup 8342 * @param sGroup {string} the name of the group 8343 */ 8344 addToGroup: function(sGroup) { 8345 this.groups[sGroup] = true; 8346 this.DDM.regDragDrop(this, sGroup); 8347 }, 8348 8349 /** 8350 * Remove's this instance from the supplied interaction group 8351 * @method removeFromGroup 8352 * @param {string} sGroup The group to drop 8353 */ 8354 removeFromGroup: function(sGroup) { 8355 this.logger.log("Removing from group: " + sGroup); 8356 if (this.groups[sGroup]) { 8357 delete this.groups[sGroup]; 8358 } 8359 8360 this.DDM.removeDDFromGroup(this, sGroup); 8361 }, 8362 8363 /** 8364 * Allows you to specify that an element other than the linked element 8365 * will be moved with the cursor during a drag 8366 * @method setDragElId 8367 * @param id {string} the id of the element that will be used to initiate the drag 8368 */ 8369 setDragElId: function(id) { 8370 this.dragElId = id; 8371 }, 8372 8373 /** 8374 * Allows you to specify a child of the linked element that should be 8375 * used to initiate the drag operation. An example of this would be if 8376 * you have a content div with text and links. Clicking anywhere in the 8377 * content area would normally start the drag operation. Use this method 8378 * to specify that an element inside of the content div is the element 8379 * that starts the drag operation. 8380 * @method setHandleElId 8381 * @param id {string} the id of the element that will be used to 8382 * initiate the drag. 8383 */ 8384 setHandleElId: function(id) { 8385 if (typeof id !== "string") { 8386 YAHOO.log("id is not a string, assuming it is an HTMLElement"); 8387 id = Dom.generateId(id); 8388 } 8389 this.handleElId = id; 8390 this.DDM.regHandle(this.id, id); 8391 }, 8392 8393 /** 8394 * Allows you to set an element outside of the linked element as a drag 8395 * handle 8396 * @method setOuterHandleElId 8397 * @param id the id of the element that will be used to initiate the drag 8398 */ 8399 setOuterHandleElId: function(id) { 8400 if (typeof id !== "string") { 8401 YAHOO.log("id is not a string, assuming it is an HTMLElement"); 8402 id = Dom.generateId(id); 8403 } 8404 this.logger.log("Adding outer handle event: " + id); 8405 Event.on(id, "mousedown", 8406 this.handleMouseDown, this, true); 8407 this.setHandleElId(id); 8408 8409 this.hasOuterHandles = true; 8410 }, 8411 8412 /** 8413 * Remove all drag and drop hooks for this element 8414 * @method unreg 8415 */ 8416 unreg: function() { 8417 this.logger.log("DragDrop obj cleanup " + this.id); 8418 Event.removeListener(this.id, "mousedown", 8419 this.handleMouseDown); 8420 this._domRef = null; 8421 this.DDM._remove(this); 8422 }, 8423 8424 /** 8425 * Returns true if this instance is locked, or the drag drop mgr is locked 8426 * (meaning that all drag/drop is disabled on the page.) 8427 * @method isLocked 8428 * @return {boolean} true if this obj or all drag/drop is locked, else 8429 * false 8430 */ 8431 isLocked: function() { 8432 return (this.DDM.isLocked() || this.locked); 8433 }, 8434 8435 /** 8436 * Fired when this object is clicked 8437 * @method handleMouseDown 8438 * @param {Event} e 8439 * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj) 8440 * @private 8441 */ 8442 handleMouseDown: function(e, oDD) { 8443 8444 var button = e.which || e.button; 8445 this.logger.log("button: " + button); 8446 8447 if (this.primaryButtonOnly && button > 1) { 8448 this.logger.log("Mousedown was not produced by the primary button"); 8449 return; 8450 } 8451 8452 if (this.isLocked()) { 8453 this.logger.log("Drag and drop is disabled, aborting"); 8454 return; 8455 } 8456 8457 this.logger.log("mousedown " + this.id); 8458 8459 this.logger.log("firing onMouseDown events"); 8460 8461 // firing the mousedown events prior to calculating positions 8462 var b4Return = this.b4MouseDown(e), 8463 b4Return2 = true; 8464 8465 if (this.events.b4MouseDown) { 8466 b4Return2 = this.fireEvent('b4MouseDownEvent', e); 8467 } 8468 var mDownReturn = this.onMouseDown(e), 8469 mDownReturn2 = true; 8470 if (this.events.mouseDown) { 8471 if (mDownReturn === false) { 8472 //Fixes #2528759 - Mousedown function returned false, don't fire the event and cancel everything. 8473 mDownReturn2 = false;
8474 } else { 8475 mDownReturn2 = this.fireEvent('mouseDownEvent', e); 8476 } 8477 } 8478 8479 if ((b4Return === false) || (mDownReturn === false) || (b4Return2 === false) || (mDownReturn2 === false)) { 8480 this.logger.log('b4MouseDown or onMouseDown returned false, exiting drag'); 8481 return; 8482 } 8483 8484 this.DDM.refreshCache(this.groups); 8485 // var self = this; 8486 // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0); 8487 8488 // Only process the event if we really clicked within the linked 8489 // element. The reason we make this check is that in the case that 8490 // another element was moved between the clicked element and the 8491 // cursor in the time between the mousedown and mouseup events. When 8492 // this happens, the element gets the next mousedown event 8493 // regardless of where on the screen it happened. 8494 var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e)); 8495 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) { 8496 this.logger.log("Click was not over the element: " + this.id); 8497 } else { 8498 if (this.clickValidator(e)) { 8499 8500 this.logger.log("click was a valid handle"); 8501 8502 // set the initial element position 8503 this.setStartPosition(); 8504 8505 // start tracking mousemove distance and mousedown time to 8506 // determine when to start the actual drag 8507 this.DDM.handleMouseDown(e, this); 8508 8509 // this mousedown is mine 8510 this.DDM.stopEvent(e); 8511 } else { 8512 8513this.logger.log("clickValidator returned false, drag not initiated"); 8514 8515 } 8516 } 8517 }, 8518 8519 /** 8520 * @method clickValidator 8521 * @description Method validates that the clicked element 8522 * was indeed the handle or a valid child of the handle 8523 * @param {Event} e 8524 */ 8525 clickValidator: function(e) { 8526 var target = YAHOO.util.Event.getTarget(e); 8527 return ( this.isValidHandleChild(target) && 8528 (this.id == this.handleElId || 8529 this.DDM.handleWasClicked(target, this.id)) ); 8530 }, 8531 8532 /** 8533 * Finds the location the element should be placed if we want to move 8534 * it to where the mouse location less the click offset would place us. 8535 * @method getTargetCoord 8536 * @param {int} iPageX the X coordinate of the click 8537 * @param {int} iPageY the Y coordinate of the click 8538 * @return an object that contains the coordinates (Object.x and Object.y) 8539 * @private 8540 */ 8541 getTargetCoord: function(iPageX, iPageY) { 8542 8543 // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY); 8544 8545 var x = iPageX - this.deltaX; 8546 var y = iPageY - this.deltaY; 8547 8548 if (this.constrainX) { 8549 if (x < this.minX) { x = this.minX; } 8550 if (x > this.maxX) { x = this.maxX; } 8551 } 8552 8553 if (this.constrainY) { 8554 if (y < this.minY) { y = this.minY; } 8555 if (y > this.maxY) { y = this.maxY; } 8556 } 8557 8558 x = this.getTick(x, this.xTicks); 8559 y = this.getTick(y, this.yTicks); 8560 8561 // this.logger.log("getTargetCoord " + 8562 // " iPageX: " + iPageX + 8563 // " iPageY: " + iPageY + 8564 // " x: " + x + ", y: " + y); 8565 8566 return {x:x, y:y}; 8567 }, 8568 8569 /** 8570 * Allows you to specify a tag name that should not start a drag operation 8571 * when clicked. This is designed to facilitate embedding links within a 8572 * drag handle that do something other than start the drag. 8573 * @method addInvalidHandleType 8574 * @param {string} tagName the type of element to exclude 8575 */ 8576 addInvalidHandleType: function(tagName) { 8577 var type = tagName.toUpperCase(); 8578 this.invalidHandleTypes[type] = type; 8579 }, 8580 8581 /** 8582 * Lets you to specify an element id for a child of a drag handle 8583 * that should not initiate a drag 8584 * @method addInvalidHandleId 8585 * @param {string} id the element id of the element you wish to ignore 8586 */ 8587 addInvalidHandleId: function(id) { 8588 if (typeof id !== "string") { 8589 YAHOO.log("id is not a string, assuming it is an HTMLElement"); 8590 id = Dom.generateId(id); 8591 } 8592 this.invalidHandleIds[id] = id; 8593 }, 8594 8595 8596 /** 8597 * Lets you specify a css class of elements that will not initiate a drag 8598 * @method addInvalidHandleClass 8599 * @param {string} cssClass the class of the elements you wish to ignore 8600 */ 8601 addInvalidHandleClass: function(cssClass) { 8602 this.invalidHandleClasses.push(cssClass); 8603 }, 8604 8605 /** 8606 * Unsets an excluded tag name set by addInvalidHandleType 8607 * @method removeInvalidHandleType 8608 * @param {string} tagName the type of element to unexclude 8609 */ 8610 removeInvalidHandleType: function(tagName) { 8611 var type = tagName.toUpperCase(); 8612 // this.invalidHandleTypes[type] = null; 8613 delete this.invalidHandleTypes[type]; 8614 }, 8615 8616 /**
8617 * Unsets an invalid handle id 8618 * @method removeInvalidHandleId 8619 * @param {string} id the id of the element to re-enable 8620 */ 8621 removeInvalidHandleId: function(id) { 8622 if (typeof id !== "string") { 8623 YAHOO.log("id is not a string, assuming it is an HTMLElement"); 8624 id = Dom.generateId(id); 8625 } 8626 delete this.invalidHandleIds[id]; 8627 }, 8628 8629 /** 8630 * Unsets an invalid css class 8631 * @method removeInvalidHandleClass 8632 * @param {string} cssClass the class of the element(s) you wish to 8633 * re-enable 8634 */ 8635 removeInvalidHandleClass: function(cssClass) { 8636 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) { 8637 if (this.invalidHandleClasses[i] == cssClass) { 8638 delete this.invalidHandleClasses[i]; 8639 } 8640 } 8641 }, 8642 8643 /** 8644 * Checks the tag exclusion list to see if this click should be ignored 8645 * @method isValidHandleChild 8646 * @param {HTMLElement} node the HTMLElement to evaluate 8647 * @return {boolean} true if this is a valid tag type, false if not 8648 */ 8649 isValidHandleChild: function(node) { 8650 8651 var valid = true; 8652 // var n = (node.nodeName == "#text") ? node.parentNode : node; 8653 var nodeName; 8654 try { 8655 nodeName = node.nodeName.toUpperCase(); 8656 } catch(e) { 8657 nodeName = node.nodeName; 8658 } 8659 valid = valid && !this.invalidHandleTypes[nodeName]; 8660 valid = valid && !this.invalidHandleIds[node.id]; 8661 8662 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) { 8663 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]); 8664 } 8665 8666 this.logger.log("Valid handle? ... " + valid); 8667 8668 return valid; 8669 8670 }, 8671 8672 /** 8673 * Create the array of horizontal tick marks if an interval was specified 8674 * in setXConstraint(). 8675 * @method setXTicks 8676 * @private 8677 */ 8678 setXTicks: function(iStartX, iTickSize) { 8679 this.xTicks = []; 8680 this.xTickSize = iTickSize; 8681 8682 var tickMap = {}; 8683 8684 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) { 8685 if (!tickMap[i]) { 8686 this.xTicks[this.xTicks.length] = i; 8687 tickMap[i] = true; 8688 } 8689 } 8690 8691 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) { 8692 if (!tickMap[i]) { 8693 this.xTicks[this.xTicks.length] = i; 8694 tickMap[i] = true; 8695 } 8696 } 8697 8698 this.xTicks.sort(this.DDM.numericSort) ; 8699 this.logger.log("xTicks: " + this.xTicks.join()); 8700 }, 8701 8702 /** 8703 * Create the array of vertical tick marks if an interval was specified in 8704 * setYConstraint(). 8705 * @method setYTicks 8706 * @private 8707 */ 8708 setYTicks: function(iStartY, iTickSize) { 8709 // this.logger.log("setYTicks: " + iStartY + ", " + iTickSize 8710 // + ", " + this.initPageY + ", " + this.minY + ", " + this.maxY ); 8711 this.yTicks = []; 8712 this.yTickSize = iTickSize; 8713 8714 var tickMap = {}; 8715 8716 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) { 8717 if (!tickMap[i]) { 8718 this.yTicks[this.yTicks.length] = i; 8719 tickMap[i] = true; 8720 } 8721 } 8722 8723 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) { 8724 if (!tickMap[i]) { 8725 this.yTicks[this.yTicks.length] = i; 8726 tickMap[i] = true; 8727 } 8728 } 8729 8730 this.yTicks.sort(this.DDM.numericSort) ; 8731 this.logger.log("yTicks: " + this.yTicks.join()); 8732 }, 8733 8734 /** 8735 * By default, the element can be dragged any place on the screen. Use 8736 * this method to limit the horizontal travel of the element. Pass in 8737 * 0,0 for the parameters if you want to lock the drag to the y axis. 8738 * @method setXConstraint 8739 * @param {int} iLeft the number of pixels the element can move to the left 8740 * @param {int} iRight the number of pixels the element can move to the 8741 * right 8742 * @param {int} iTickSize optional parameter for specifying that the 8743 * element 8744 * should move iTickSize pixels at a time. 8745 */ 8746 setXConstraint: function(iLeft, iRight, iTickSize) { 8747 this.leftConstraint = parseInt(iLeft, 10); 8748 this.rightConstraint = parseInt(iRight, 10); 8749 8750 this.minX = this.initPageX - this.leftConstraint; 8751 this.maxX = this.initPageX + this.rightConstraint; 8752 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); } 8753 8754 this.constrainX = true; 8755 this.logger.log("initPageX:" + this.initPageX + " minX:" + this.minX + 8756 " maxX:" + this.maxX); 8757 }, 8758 8759 /** 8760 * Clears any constraints applied to this instance. Also clears ticks 8761 * since they can't exist independent of a constraint at this time. 8762 * @method clearConstraints 8763 */ 8764 clearConstraints: function() { 8765 this.logger.log("Clearing constraints"); 8766 this.constrainX = false;
8767 this.constrainY = false; 8768 this.clearTicks(); 8769 }, 8770 8771 /** 8772 * Clears any tick interval defined for this instance 8773 * @method clearTicks 8774 */ 8775 clearTicks: function() { 8776 this.logger.log("Clearing ticks"); 8777 this.xTicks = null; 8778 this.yTicks = null; 8779 this.xTickSize = 0; 8780 this.yTickSize = 0; 8781 }, 8782 8783 /** 8784 * By default, the element can be dragged any place on the screen. Set 8785 * this to limit the vertical travel of the element. Pass in 0,0 for the 8786 * parameters if you want to lock the drag to the x axis. 8787 * @method setYConstraint 8788 * @param {int} iUp the number of pixels the element can move up 8789 * @param {int} iDown the number of pixels the element can move down 8790 * @param {int} iTickSize optional parameter for specifying that the 8791 * element should move iTickSize pixels at a time. 8792 */ 8793 setYConstraint: function(iUp, iDown, iTickSize) { 8794 this.logger.log("setYConstraint: " + iUp + "," + iDown + "," + iTickSize); 8795 this.topConstraint = parseInt(iUp, 10); 8796 this.bottomConstraint = parseInt(iDown, 10); 8797 8798 this.minY = this.initPageY - this.topConstraint; 8799 this.maxY = this.initPageY + this.bottomConstraint; 8800 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); } 8801 8802 this.constrainY = true; 8803 8804 this.logger.log("initPageY:" + this.initPageY + " minY:" + this.minY + 8805 " maxY:" + this.maxY); 8806 }, 8807 8808 /** 8809 * resetConstraints must be called if you manually reposition a dd element. 8810 * @method resetConstraints 8811 */ 8812 resetConstraints: function() { 8813 8814 //this.logger.log("resetConstraints"); 8815 8816 // Maintain offsets if necessary 8817 if (this.initPageX || this.initPageX === 0) { 8818 //this.logger.log("init pagexy: " + this.initPageX + ", " + 8819 //this.initPageY); 8820 //this.logger.log("last pagexy: " + this.lastPageX + ", " + 8821 //this.lastPageY); 8822 // figure out how much this thing has moved 8823 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0; 8824 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0; 8825 8826 this.setInitPosition(dx, dy); 8827 8828 // This is the first time we have detected the element's position 8829 } else { 8830 this.setInitPosition(); 8831 } 8832 8833 if (this.constrainX) { 8834 this.setXConstraint( this.leftConstraint, 8835 this.rightConstraint, 8836 this.xTickSize ); 8837 } 8838 8839 if (this.constrainY) { 8840 this.setYConstraint( this.topConstraint, 8841 this.bottomConstraint, 8842 this.yTickSize ); 8843 } 8844 }, 8845 8846 /** 8847 * Normally the drag element is moved pixel by pixel, but we can specify 8848 * that it move a number of pixels at a time. This method resolves the 8849 * location when we have it set up like this. 8850 * @method getTick 8851 * @param {int} val where we want to place the object 8852 * @param {int[]} tickArray sorted array of valid points 8853 * @return {int} the closest tick 8854 * @private 8855 */ 8856 getTick: function(val, tickArray) { 8857 8858 if (!tickArray) { 8859 // If tick interval is not defined, it is effectively 1 pixel, 8860 // so we return the value passed to us. 8861 return val; 8862 } else if (tickArray[0] >= val) { 8863 // The value is lower than the first tick, so we return the first 8864 // tick. 8865 return tickArray[0]; 8866 } else { 8867 for (var i=0, len=tickArray.length; i<len; ++i) { 8868 var next = i + 1; 8869 if (tickArray[next] && tickArray[next] >= val) { 8870 var diff1 = val - tickArray[i]; 8871 var diff2 = tickArray[next] - val; 8872 return (diff2 > diff1) ? tickArray[i] : tickArray[next]; 8873 } 8874 } 8875 8876 // The value is larger than the last tick, so we return the last 8877 // tick. 8878 return tickArray[tickArray.length - 1]; 8879 } 8880 }, 8881 8882 /** 8883 * toString method 8884 * @method toString 8885 * @return {string} string representation of the dd obj 8886 */ 8887 toString: function() { 8888 return ("DragDrop " + this.id); 8889 } 8890 8891}; 8892YAHOO.augment(YAHOO.util.DragDrop, YAHOO.util.EventProvider); 8893 8894/** 8895* @event mouseDownEvent 8896* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation. 8897* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8898*/ 8899 8900/** 8901* @event b4MouseDownEvent 8902* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag. 8903* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8904*/ 8905 8906/** 8907* @event mouseUpEvent 8908* @description Fired from inside DragDropMgr when the drag operation is finished. 8909* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8910*/ 8911 8912/** 8913* @event b4StartDragEvent 8914* @description Fires before the startDragEvent, returning false will cancel the startDrag Event. 8915* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8916*/ 8917 8918/** 8919* @event startDragEvent 8920* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 8921* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8922*/ 8923 8924/** 8925* @event b4EndDragEvent 8926* @description Fires before the endDragEvent. Returning false will cancel. 8927* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8928*/ 8929 8930/** 8931* @event endDragEvent 8932* @description Fires on the mouseup event after a drag has been initiated (startDrag fired). 8933* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8934*/ 8935 8936/** 8937* @event dragEvent 8938* @description Occurs every mousemove event while dragging. 8939* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8940*/ 8941/** 8942* @event b4DragEvent 8943* @description Fires before the dragEvent. 8944* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8945*/ 8946/** 8947* @event invalidDropEvent 8948* @description Fires when the dragged objects is dropped in a location that contains
8948no drop targets. 8949* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8950*/ 8951/** 8952* @event b4DragOutEvent 8953* @description Fires before the dragOutEvent 8954* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8955*/ 8956/** 8957* @event dragOutEvent 8958* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 8959* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8960*/ 8961/** 8962* @event dragEnterEvent 8963* @description Occurs when the dragged object first interacts with another targettable drag and drop object. 8964* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8965*/ 8966/** 8967* @event b4DragOverEvent 8968* @description Fires before the dragOverEvent. 8969* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8970*/ 8971/** 8972* @event dragOverEvent 8973* @description Fires every mousemove event while over a drag and drop object. 8974* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8975*/ 8976/** 8977* @event b4DragDropEvent 8978* @description Fires before the dragDropEvent 8979* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8980*/ 8981/** 8982* @event dragDropEvent 8983* @description Fires when the dragged objects is dropped on another. 8984* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 8985*/ 8986})(); 8987/** 8988 * A DragDrop implementation where the linked element follows the 8989 * mouse cursor during a drag. 8990 * @class DD 8991 * @extends YAHOO.util.DragDrop 8992 * @constructor 8993 * @param {String} id the id of the linked element 8994 * @param {String} sGroup the group of related DragDrop items 8995 * @param {object} config an object containing configurable attributes 8996 * Valid properties for DD: 8997 * scroll 8998 */ 8999YAHOO.util.DD = function(id, sGroup, config) { 9000 if (id) { 9001 this.init(id, sGroup, config); 9002 } 9003}; 9004 9005YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, { 9006 9007 /** 9008 * When set to true, the utility automatically tries to scroll the browser 9009 * window when a drag and drop element is dragged near the viewport boundary. 9010 * Defaults to true. 9011 * @property scroll 9012 * @type boolean 9013 */ 9014 scroll: true, 9015 9016 /** 9017 * Sets the pointer offset to the distance between the linked element's top 9018 * left corner and the location the element was clicked 9019 * @method autoOffset 9020 * @param {int} iPageX the X coordinate of the click 9021 * @param {int} iPageY the Y coordinate of the click 9022 */ 9023 autoOffset: function(iPageX, iPageY) { 9024 var x = iPageX - this.startPageX; 9025 var y = iPageY - this.startPageY; 9026 this.setDelta(x, y); 9027 // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y); 9028 }, 9029 9030 /** 9031 * Sets the pointer offset. You can call this directly to force the 9032 * offset to be in a particular location (e.g., pass in 0,0 to set it 9033 * to the center of the object, as done in YAHOO.widget.Slider) 9034 * @method setDelta 9035 * @param {int} iDeltaX the distance from the left 9036 * @param {int} iDeltaY the distance from the top 9037 */ 9038 setDelta: function(iDeltaX, iDeltaY) { 9039 this.deltaX = iDeltaX; 9040 this.deltaY = iDeltaY; 9041 this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY); 9042 }, 9043 9044 /** 9045 * Sets the drag element to the location of the mousedown or click event, 9046 * maintaining the cursor location relative to the location on the element 9047 * that was clicked. Override this if you want to place the element in a 9048 * location other than where the cursor is. 9049 * @method setDragElPos 9050 * @param {int} iPageX the X coordinate of the mousedown or drag event 9051 * @param {int} iPageY the Y coordinate of the mousedown or drag event 9052 */ 9053 setDragElPos: function(iPageX, iPageY) { 9054 // the first time we do this, we are going to check to make sure 9055 // the element has css positioning 9056 9057 var el = this.getDragEl(); 9058 this.alignElWithMouse(el, iPageX, iPageY); 9059 }, 9060 9061 /** 9062 * Sets the element to the location of the mousedown or click event, 9063 * maintaining the cursor location relative to the location on the element 9064 * that was clicked. Override this if you want to place the element in a 9065 * location other than where the cursor is. 9066 * @method alignElWithMouse 9067 * @param {HTMLElement} el the element to move 9068 * @param {int} iPageX the X coordinate of the mousedown or drag event 9069 * @param {int} iPageY the Y coordinate of the mousedown or drag event 9070 */ 9071 alignElWithMouse: function(el, iPageX, iPageY) { 9072 var oCoord = this.getTargetCoord(iPageX, iPageY); 9073 // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display); 9074 9075 if (!this.deltaSetXY) { 9076 var aCoord = [oCoord.x, oCoord.y]; 9077 YAHOO.util.Dom.setXY(el, aCoord); 9078 9079 var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 ); 9080 var newTop = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 ); 9081 9082 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ]; 9083 } else { 9084 YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px"); 9085 YAHOO.util.Dom.setStyle(el, "top", (oCoord.y + this.deltaSetXY[1]) + "px"); 9086 } 9087 9088 this.cachePosition(oCoord.x, oCoord.y); 9089 var self = this; 9090 setTimeout(function() { 9091 self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth); 9092 }, 0); 9093 }, 9094 9095 /** 9096 * Saves the most recent position so that we can reset the constraints and 9097 * tick marks on-demand. We need to know this so that we can calculate the 9098 * number of pixels the element is offset from its original position. 9099 * @method cachePosition 9100 * @param iPageX the current x position (optional, this just makes it so we 9101 * don't have to look it up again) 9102 * @param iPageY the current y position (optional, this just makes it so we 9103 * don't have to look it up again) 9104 */ 9105 cachePosition: function(iPageX, iPageY) { 9106 if (iPageX) { 9107 this.lastPageX = iPageX; 9108 this.lastPageY = iPageY; 9109 } else { 9110 var aCoord = YAHOO.util.Dom.getXY(this.getEl()); 9111 this.lastPageX = aCoord[0]; 9112 this.lastPageY = aCoord[1]; 9113 } 9114 }, 9115 9116 /** 9117 * Auto-scroll the window if the dragged object has been moved beyond the 9118 * visible window boundary. 9119 * @method autoScroll 9120 * @param {int} x the drag element's x position 9121 * @param {int} y the drag element's y position 9122 * @param {int} h the height of the drag element 9123 * @param {int} w the width of the drag element 9124 * @private 9125 */ 9126 autoScroll: function(x, y, h, w) { 9127 9128 if (this.scroll) { 9129 // The client height 9130 var clientH = this.DDM.getClientHeight(); 9131 9132 // The client width 9133 var clientW = this.DDM.getClientWidth(); 9134 9135 // The amt scrolled down 9136 var st = this.DDM.getScrollTop(); 9137 9138 // The amt scrolled right 9139 var sl = this.DDM.getScrollLeft(); 9140 9141 // Location of the bottom of the element 9142 var bot = h + y; 9143 9144 // Location of the right of the element 9145 var right = w + x; 9146 9147 // The distance from the cursor to the bottom of the visible area, 9148 // adjusted so that we don't scroll if the cursor is beyond the 9149 // element drag constraints 9150 var toBot = (clientH + st - y - this.deltaY); 9151 9152 // The distance from the cursor to the right of the visible area 9153 var toRight = (clientW + sl - x - this.deltaX); 9154 9155 // this.logger.log( " x: " + x + " y: " + y + " h: " + h + 9156 // " clientH: " + clientH + " clientW: " + clientW + 9157 // " st: " + st + " sl: " + sl + " bot: " + bot + 9158 // " right: " + right + " toBot: " + toBot + " toRight: " + toRight); 9159 9160 // How close to the edge the cursor must be before we scroll 9161 // var thresh = (document.all) ? 100 : 40; 9162 var thresh = 40; 9163 9164 // How many pixels to scroll per autoscroll op. This helps to reduce 9165 // clunky scrolling. IE is more sensitive about this ... it needs this 9166 // value to be higher. 9167 var scrAmt = (document.all) ? 80 : 30; 9168 9169 // Scroll down if we are near the bottom of the visible page and the 9170 // obj extends below the crease 9171 if ( bot > clientH && toBot < thresh ) { 9172 window.scrollTo(sl, st + scrAmt); 9173 } 9174 9175 // Scroll up if the window is scrolled down and the top of the object 9176 // goes above the top border 9177 if ( y < st && st > 0 && y - st < thresh ) { 9178 window.scrollTo(sl, st - scrAmt); 9179 } 9180 9181 // Scroll right if the obj is beyond the right border and the cursor is 9182 // near the border. 9183 if ( right > clientW && toRight < thresh ) { 9184 window.scrollTo(sl + scrAmt, st); 9185 } 9186 9187 // Scroll left if the window has been scrolled to the right and the obj 9188 // extends past the left border 9189 if ( x < sl && sl > 0 && x - sl < thresh ) { 9190 window.scrollTo(sl - scrAmt, st); 9191 } 9192 } 9193 }, 9194 9195 /* 9196 * Sets up config options specific to this class. Overrides 9197 * YAHOO.util.DragDrop, but all versions of this method through the 9198 * inheritance chain are called 9199 */ 9200 applyConfig: function() { 9201 YAHOO.util.DD.superclass.applyConfig.call(this); 9202 this.scroll = (this.config.scroll !== false); 9203 }, 9204 9205 /* 9206 * Event that fires prior to the onMouseDown event. Overrides 9207 * YAHOO.util.DragDrop. 9208 */ 9209 b4MouseDown: function(e) { 9210 this.setStartPosition(); 9211 // this.resetConstraints(); 9212 this.autoOffset(YAHOO.util.Event.getPageX(e), 9213 YAHOO.util.Event.getPageY(e)); 9214 }, 9215 9216 /* 9217 * Event that fires prior to the onDrag event. Overrides 9218 * YAHOO.util.DragDrop. 9219 */ 9220 b4Drag: function(e) { 9221 this.setDragElPos(YAHOO.util.Event.getPageX(e), 9222 YAHOO.util.Event.getPageY(e)); 9223 }, 9224 9225 toString: function() { 9226 return ("DD " + this.id); 9227 } 9228
9229 ////////////////////////////////////////////////////////////////////////// 9230 // Debugging ygDragDrop events that can be overridden 9231 ////////////////////////////////////////////////////////////////////////// 9232 /* 9233 startDrag: function(x, y) { 9234 this.logger.log(this.id.toString() + " startDrag"); 9235 }, 9236 9237 onDrag: function(e) { 9238 this.logger.log(this.id.toString() + " onDrag"); 9239 }, 9240 9241 onDragEnter: function(e, id) { 9242 this.logger.log(this.id.toString() + " onDragEnter: " + id); 9243 }, 9244 9245 onDragOver: function(e, id) { 9246 this.logger.log(this.id.toString() + " onDragOver: " + id); 9247 }, 9248 9249 onDragOut: function(e, id) { 9250 this.logger.log(this.id.toString() + " onDragOut: " + id); 9251 }, 9252 9253 onDragDrop: function(e, id) { 9254 this.logger.log(this.id.toString() + " onDragDrop: " + id); 9255 }, 9256 9257 endDrag: function(e) { 9258 this.logger.log(this.id.toString() + " endDrag"); 9259 } 9260 9261 */ 9262 9263/** 9264* @event mouseDownEvent 9265* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation. 9266* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9267*/ 9268 9269/** 9270* @event b4MouseDownEvent 9271* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag. 9272* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9273*/ 9274 9275/** 9276* @event mouseUpEvent 9277* @description Fired from inside DragDropMgr when the drag operation is finished. 9278* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9279*/ 9280 9281/** 9282* @event b4StartDragEvent 9283* @description Fires before the startDragEvent, returning false will cancel the startDrag Event. 9284* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9285*/ 9286 9287/** 9288* @event startDragEvent 9289* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 9290* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9291*/ 9292 9293/** 9294* @event b4EndDragEvent 9295* @description Fires before the endDragEvent. Returning false will cancel. 9296* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9297*/ 9298 9299/** 9300* @event endDragEvent 9301* @description Fires on the mouseup event after a drag has been initiated (startDrag fired). 9302* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9303*/ 9304 9305/** 9306* @event dragEvent 9307* @description Occurs every mousemove event while dragging. 9308* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9309*/ 9310/** 9311* @event b4DragEvent 9312* @description Fires before the dragEvent. 9313* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9314*/ 9315/** 9316* @event invalidDropEvent 9317* @description Fires when the dragged objects is dropped in a location that contains no drop targets. 9318* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9319*/ 9320/** 9321* @event b4DragOutEvent 9322* @description Fires before the dragOutEvent 9323* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9324*/ 9325/** 9326* @event dragOutEvent 9327* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 9328* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9329*/ 9330/** 9331* @event dragEnterEvent 9332* @description Occurs when the dragged object first interacts with another targettable drag and drop object. 9333* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9334*/ 9335/** 9336* @event b4DragOverEvent 9337* @description Fires before the dragOverEvent. 9338* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9339*/ 9340/** 9341* @event dragOverEvent 9342* @description Fires every mousemove event while over a drag and drop object. 9343* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9344*/ 9345/** 9346* @event b4DragDropEvent 9347* @description Fires before the dragDropEvent 9348* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9349*/ 9350/** 9351* @event dragDropEvent 9352* @description Fires when the dragged objects is dropped on another. 9353* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9354*/ 9355}); 9356/** 9357 * A DragDrop implementation that inserts an empty, bordered div into 9358 * the document that follows the cursor during drag operations. At the time of 9359 * the click, the frame div is resized to the dimensions of the linked html 9360 * element, and moved to the exact location of the linked element. 9361 * 9362 * References to the "frame" element refer to the single proxy element that 9363 * was created to be dragged in place of all DDProxy elements on the 9364 * page. 9365 * 9366 * @class DDProxy 9367 * @extends YAHOO.util.DD 9368 * @constructor 9369 * @param {String} id the id of the linked html element 9370 * @param {String} sGroup the group of related DragDrop objects 9371 * @param {object}
9371 config an object containing configurable attributes 9372 * Valid properties for DDProxy in addition to those in DragDrop: 9373 * resizeFrame, centerFrame, dragElId 9374 */ 9375YAHOO.util.DDProxy = function(id, sGroup, config) { 9376 if (id) { 9377 this.init(id, sGroup, config); 9378 this.initFrame(); 9379 } 9380}; 9381 9382/** 9383 * The default drag frame div id 9384 * @property YAHOO.util.DDProxy.dragElId 9385 * @type String 9386 * @static 9387 */ 9388YAHOO.util.DDProxy.dragElId = "ygddfdiv"; 9389 9390YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, { 9391 9392 /** 9393 * By default we resize the drag frame to be the same size as the element 9394 * we want to drag (this is to get the frame effect). We can turn it off 9395 * if we want a different behavior. 9396 * @property resizeFrame 9397 * @type boolean 9398 */ 9399 resizeFrame: true, 9400 9401 /** 9402 * By default the frame is positioned exactly where the drag element is, so 9403 * we use the cursor offset provided by YAHOO.util.DD. Another option that works only if 9404 * you do not have constraints on the obj is to have the drag frame centered 9405 * around the cursor. Set centerFrame to true for this effect. 9406 * @property centerFrame 9407 * @type boolean 9408 */ 9409 centerFrame: false, 9410 9411 /** 9412 * Creates the proxy element if it does not yet exist 9413 * @method createFrame 9414 */ 9415 createFrame: function() { 9416 var self=this, body=document.body; 9417 9418 if (!body || !body.firstChild) { 9419 setTimeout( function() { self.createFrame(); }, 50 ); 9420 return; 9421 } 9422 9423 var div=this.getDragEl(), Dom=YAHOO.util.Dom; 9424 9425 if (!div) { 9426 div = document.createElement("div"); 9427 div.id = this.dragElId; 9428 var s = div.style; 9429 9430 s.position = "absolute"; 9431 s.visibility = "hidden"; 9432 s.cursor = "move"; 9433 s.border = "2px solid #aaa"; 9434 s.zIndex = 999; 9435 s.height = "25px"; 9436 s.width = "25px"; 9437 9438 var _data = document.createElement('div'); 9439 Dom.setStyle(_data, 'height', '100%'); 9440 Dom.setStyle(_data, 'width', '100%'); 9441 /** 9442 * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer. 9443 * Since it is "transparent" then the events pass through it to the iframe below. 9444 * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an 9445 * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through. 9446 */ 9447 Dom.setStyle(_data, 'background-color', '#ccc'); 9448 Dom.setStyle(_data, 'opacity', '0'); 9449 div.appendChild(_data); 9450 9451 // appendChild can blow up IE if invoked prior to the window load event 9452 // while rendering a table. It is possible there are other scenarios 9453 // that would cause this to happen as well. 9454 body.insertBefore(div, body.firstChild); 9455 } 9456 }, 9457 9458 /** 9459 * Initialization for the drag frame element. Must be called in the 9460 * constructor of all subclasses 9461 * @method initFrame 9462 */ 9463 initFrame: function() { 9464 this.createFrame(); 9465 }, 9466 9467 applyConfig: function() { 9468 //this.logger.log("DDProxy applyConfig"); 9469 YAHOO.util.DDProxy.superclass.applyConfig.call(this); 9470 9471 this.resizeFrame = (this.config.resizeFrame !== false); 9472 this.centerFrame = (this.config.centerFrame); 9473 this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId); 9474 }, 9475 9476 /** 9477 * Resizes the drag frame to the dimensions of the clicked object, positions 9478 * it over the object, and finally displays it 9479 * @method showFrame 9480 * @param {int} iPageX X click position 9481 * @param {int} iPageY Y click position 9482 * @private 9483 */ 9484 showFrame: function(iPageX, iPageY) { 9485 var el = this.getEl(); 9486 var dragEl = this.getDragEl(); 9487 var s = dragEl.style; 9488 9489 this._resizeProxy(); 9490 9491 if (this.centerFrame) { 9492 this.setDelta( Math.round(parseInt(s.width, 10)/2), 9493 Math.round(parseInt(s.height, 10)/2) ); 9494 } 9495 9496 this.setDragElPos(iPageX, iPageY); 9497 9498 YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 9499 }, 9500 9501 /** 9502 * The proxy is automatically resized to the dimensions of the linked 9503 * element when a drag is initiated, unless resizeFrame is set to false 9504 * @method _resizeProxy 9505 * @private 9506 */ 9507 _resizeProxy: function() { 9508 if (this.resizeFrame) { 9509 var DOM = YAHOO.util.Dom; 9510 var el = this.getEl(); 9511 var dragEl = this.getDragEl(); 9512 9513 var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth" ), 10); 9514 var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth" ), 10); 9515 var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10); 9516 var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth" ), 10); 9517 9518 if (isNaN(bt)) { bt = 0; } 9519 if (isNaN(br)) { br = 0; } 9520 if (isNaN(bb)) { bb = 0; } 9521 if (isNaN(bl)) { bl = 0; } 9522 9523 this.logger.log("proxy size: " + bt + " " + br + " " + bb + " " + bl); 9524 9525 var newWidth = Math.max(0, el.offsetWidth - br - bl); 9526 var newHeight = Math.max(0, el.offsetHeight - bt - bb); 9527 9528 this.logger.log("Resizing proxy element"); 9529 9530 DOM.setStyle( dragEl, "width", newWidth + "px" ); 9531 DOM.setStyle( dragEl, "height", newHeight + "px" ); 9532 } 9533 }, 9534 9535 // overrides YAHOO.util.DragDrop 9536 b4MouseDown: function(e) { 9537 this.setStartPosition(); 9538 var x = YAHOO.util.Event.getPageX(e); 9539 var y = YAHOO.util.Event.getPageY(e); 9540 this.autoOffset(x, y); 9541 9542 // This causes the autoscroll code to kick off, which means autoscroll can 9543 // happen prior to the check for a valid drag handle. 9544 // this.setDragElPos(x, y); 9545 }, 9546 9547 // overrides YAHOO.util.DragDrop 9548 b4StartDrag: function(x, y) { 9549 // show the drag frame 9550 this.logger.log("start drag show frame, x: " + x + ", y: " + y);
9551 this.showFrame(x, y); 9552 }, 9553 9554 // overrides YAHOO.util.DragDrop 9555 b4EndDrag: function(e) { 9556 this.logger.log(this.id + " b4EndDrag"); 9557 YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 9558 }, 9559 9560 // overrides YAHOO.util.DragDrop 9561 // By default we try to move the element to the last location of the frame. 9562 // This is so that the default behavior mirrors that of YAHOO.util.DD. 9563 endDrag: function(e) { 9564 var DOM = YAHOO.util.Dom; 9565 this.logger.log(this.id + " endDrag"); 9566 var lel = this.getEl(); 9567 var del = this.getDragEl(); 9568 9569 // Show the drag frame briefly so we can get its position 9570 // del.style.visibility = ""; 9571 DOM.setStyle(del, "visibility", ""); 9572 9573 // Hide the linked element before the move to get around a Safari 9574 // rendering bug. 9575 //lel.style.visibility = "hidden"; 9576 DOM.setStyle(lel, "visibility", "hidden"); 9577 YAHOO.util.DDM.moveToEl(lel, del); 9578 //del.style.visibility = "hidden"; 9579 DOM.setStyle(del, "visibility", "hidden"); 9580 //lel.style.visibility = ""; 9581 DOM.setStyle(lel, "visibility", ""); 9582 }, 9583 9584 toString: function() { 9585 return ("DDProxy " + this.id); 9586 } 9587/** 9588* @event mouseDownEvent 9589* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation. 9590* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9591*/ 9592 9593/** 9594* @event b4MouseDownEvent 9595* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag. 9596* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9597*/ 9598 9599/** 9600* @event mouseUpEvent 9601* @description Fired from inside DragDropMgr when the drag operation is finished. 9602* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9603*/ 9604 9605/** 9606* @event b4StartDragEvent 9607* @description Fires before the startDragEvent, returning false will cancel the startDrag Event. 9608* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9609*/ 9610 9611/** 9612* @event startDragEvent 9613* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 9614* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9615*/ 9616 9617/** 9618* @event b4EndDragEvent 9619* @description Fires before the endDragEvent. Returning false will cancel. 9620* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9621*/ 9622 9623/** 9624* @event endDragEvent 9625* @description Fires on the mouseup event after a drag has been initiated (startDrag fired). 9626* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9627*/ 9628 9629/** 9630* @event dragEvent 9631* @description Occurs every mousemove event while dragging. 9632* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9633*/ 9634/** 9635* @event b4DragEvent 9636* @description Fires before the dragEvent. 9637* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9638*/ 9639/** 9640* @event invalidDropEvent 9641* @description Fires when the dragged objects is dropped in a location that contains no drop targets. 9642* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9643*/ 9644/** 9645* @event b4DragOutEvent 9646* @description Fires before the dragOutEvent 9647* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9648*/ 9649/** 9650* @event dragOutEvent 9651* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 9652* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9653*/ 9654/** 9655* @event dragEnterEvent 9656* @description Occurs when the dragged object first interacts with another targettable drag and drop object. 9657* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9658*/ 9659/** 9660* @event b4DragOverEvent 9661* @description Fires before the dragOverEvent. 9662* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9663*/ 9664/** 9665* @event dragOverEvent 9666* @description Fires every mousemove event while over a drag and drop object. 9667* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9668*/ 9669/** 9670* @event b4DragDropEvent 9671* @description Fires before the dragDropEvent 9672* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9673*/ 9674/** 9675* @event dragDropEvent 9676* @description Fires when the dragged objects is dropped on another. 9677* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event. 9678*/ 9679 9680}); 9681/** 9682 * A DragDrop implementation that does not move, but can be a drop 9683 * target. You would get the same result by simply omitting implementation 9684 * for the event callbacks, but this way we reduce the processing cost of the 9685 * event listener and the callbacks. 9686 * @class DDTarget 9687 * @extends YAHOO.util.DragDrop 9688 * @constructor 9689 * @param {String} id the id of the element that is a drop target 9690 * @param {String} sGroup the group of related DragDrop objects 9691 * @param {object}
9691 config an object containing configurable attributes 9692 * Valid properties for DDTarget in addition to those in 9693 * DragDrop: 9694 * none 9695 */ 9696YAHOO.util.DDTarget = function(id, sGroup, config) { 9697 if (id) { 9698 this.initTarget(id, sGroup, config); 9699 } 9700}; 9701 9702// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop(); 9703YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, { 9704 toString: function() { 9705 return ("DDTarget " + this.id); 9706 } 9707}); 9708YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.9.0", build: "2800"}); 9709/* 9710Copyright (c) 2011, Yahoo! Inc. All rights reserved. 9711Code licensed under the BSD License: 9712http://developer.yahoo.com/yui/license.html 9713version: 2.9.0 9714*/ 9715(function() { 9716 9717var Y = YAHOO.util; 9718 9719/* 9720Copyright (c) 2006, Yahoo! Inc. All rights reserved. 9721Code licensed under the BSD License: 9722http://developer.yahoo.net/yui/license.txt 9723*/ 9724 9725/** 9726 * The animation module provides allows effects to be added to HTMLElements. 9727 * @module animation 9728 * @requires yahoo, event, dom 9729 */ 9730 9731/** 9732 * 9733 * Base animation class that provides the interface for building animated effects. 9734 * <p>Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);</p> 9735 * @class Anim 9736 * @namespace YAHOO.util 9737 * @requires YAHOO.util.AnimMgr 9738 * @requires YAHOO.util.Easing 9739 * @requires YAHOO.util.Dom 9740 * @requires YAHOO.util.Event 9741 * @requires YAHOO.util.CustomEvent 9742 * @constructor 9743 * @param {String | HTMLElement} el Reference to the element that will be animated 9744 * @param {Object} attributes The attribute(s) to be animated. 9745 * Each attribute is an object with at minimum a "to" or "by" member defined. 9746 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). 9747 * All attribute names use camelCase. 9748 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based 9749 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method) 9750 */ 9751 9752var Anim = function(el, attributes, duration, method) { 9753 if (!el) { 9754 YAHOO.log('element required to create Anim instance', 'error', 'Anim'); 9755 } 9756 this.init(el, attributes, duration, method); 9757}; 9758 9759Anim.NAME = 'Anim'; 9760 9761Anim.prototype = { 9762 /** 9763 * Provides a readable name for the Anim instance. 9764 * @method toString 9765 * @return {String} 9766 */ 9767 toString: function() { 9768 var el = this.getEl() || {}; 9769 var id = el.id || el.tagName; 9770 return (this.constructor.NAME + ': ' + id); 9771 }, 9772 9773 patterns: { // cached for performance 9774 noNegatives: /width|height|opacity|padding/i, // keep at zero or above 9775 offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default 9776 defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default 9777 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset 9778 }, 9779 9780 /** 9781 * Returns the value computed by the animation's "method". 9782 * @method doMethod 9783 * @param {String} attr The name of the attribute. 9784 * @param {Number} start The value this attribute should start from for this animation. 9785 * @param {Number} end The value this attribute should end at for this animation. 9786 * @return {Number} The Value to be applied to the attribute. 9787 */ 9788 doMethod: function(attr, start, end) { 9789 return this.method(this.currentFrame, start, end - start, this.totalFrames); 9790 }, 9791 9792 /** 9793 * Applies a value to an attribute. 9794 * @method setAttribute 9795 * @param {String} attr The name of the attribute. 9796 * @param {Number} val The value to be applied to the attribute. 9797 * @param {String} unit The unit ('px', '%', etc.) of the value. 9798 */ 9799 setAttribute: function(attr, val, unit) { 9800 var el = this.getEl(); 9801 if ( this.patterns.noNegatives.test(attr) ) { 9802 val = (val > 0) ? val : 0; 9803 } 9804 9805 if (attr in el && !('style' in el && attr in el.style)) { 9806 el[attr] = val; 9807 } else { 9808 Y.Dom.setStyle(el, attr, val + unit); 9809 } 9810 }, 9811 9812 /** 9813 * Returns current value of the attribute. 9814 * @method getAttribute 9815 * @param {String} attr The name of the attribute. 9816 * @return {Number} val The current value of the attribute. 9817 */ 9818 getAttribute: function(attr) { 9819 var el = this.getEl(); 9820 var val = Y.Dom.getStyle(el, attr); 9821 9822 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { 9823 return parseFloat(val); 9824 } 9825 9826 var a = this.patterns.offsetAttribute.exec(attr) || []; 9827 var pos = !!( a[3] ); // top or left 9828 var box = !!( a[2] ); // width or height 9829 9830 if ('style' in el) { 9831 // use offsets for width/height and abs pos top/left 9832 if ( box || (Y.Dom.getStyle(el, 'position') == 'absolute' && pos) ) { 9833 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; 9834 } else { // default to zero for other 'auto' 9835 val = 0; 9836 } 9837 } else if (attr in el) { 9838 val = el[attr]; 9839 } 9840 9841 return val; 9842 }, 9843 9844 /** 9845 * Returns the unit to use when none is supplied.
9846 * @method getDefaultUnit 9847 * @param {attr} attr The name of the attribute. 9848 * @return {String} The default unit to be used. 9849 */ 9850 getDefaultUnit: function(attr) { 9851 if ( this.patterns.defaultUnit.test(attr) ) { 9852 return 'px'; 9853 } 9854 9855 return ''; 9856 }, 9857 9858 /** 9859 * Sets the actual values to be used during the animation. Should only be needed for subclass use. 9860 * @method setRuntimeAttribute 9861 * @param {Object} attr The attribute object 9862 * @private 9863 */ 9864 setRuntimeAttribute: function(attr) { 9865 var start; 9866 var end; 9867 var attributes = this.attributes; 9868 9869 this.runtimeAttributes[attr] = {}; 9870 9871 var isset = function(prop) { 9872 return (typeof prop !== 'undefined'); 9873 }; 9874 9875 if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { 9876 return false; // note return; nothing to animate to 9877 } 9878 9879 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); 9880 9881 // To beats by, per SMIL 2.1 spec 9882 if ( isset(attributes[attr]['to']) ) { 9883 end = attributes[attr]['to']; 9884 } else if ( isset(attributes[attr]['by']) ) { 9885 if (start.constructor == Array) { 9886 end = []; 9887 for (var i = 0, len = start.length; i < len; ++i) { 9888 end[i] = start[i] + attributes[attr]['by'][i] * 1; // times 1 to cast "by" 9889 } 9890 } else { 9891 end = start + attributes[attr]['by'] * 1; 9892 } 9893 } 9894 9895 this.runtimeAttributes[attr].start = start; 9896 this.runtimeAttributes[attr].end = end; 9897 9898 // set units if needed 9899 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? 9900 attributes[attr]['unit'] : this.getDefaultUnit(attr); 9901 return true; 9902 }, 9903 9904 /** 9905 * Constructor for Anim instance. 9906 * @method init 9907 * @param {String | HTMLElement} el Reference to the element that will be animated 9908 * @param {Object} attributes The attribute(s) to be animated. 9909 * Each attribute is an object with at minimum a "to" or "by" member defined. 9910 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). 9911 * All attribute names use camelCase. 9912 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based 9913 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method) 9914 */ 9915 init: function(el, attributes, duration, method) { 9916 /** 9917 * Whether or not the animation is running. 9918 * @property isAnimated 9919 * @private 9920 * @type Boolean 9921 */ 9922 var isAnimated = false; 9923 9924 /** 9925 * A Date object that is created when the animation begins. 9926 * @property startTime 9927 * @private 9928 * @type Date 9929 */ 9930 var startTime = null; 9931 9932 /** 9933 * The number of frames this animation was able to execute. 9934 * @property actualFrames 9935 * @private 9936 * @type Int 9937 */ 9938 var actualFrames = 0; 9939 9940 /** 9941 * The element to be animated. 9942 * @property el 9943 * @private 9944 * @type HTMLElement 9945 */ 9946 el = Y.Dom.get(el); 9947 9948 /** 9949 * The collection of attributes to be animated. 9950 * Each attribute must have at least a "to" or "by" defined in order to animate. 9951 * If "to" is supplied, the animation will end with the attribute at that value. 9952 * If "by" is supplied, the animation will end at that value plus its starting value. 9953 * If both are supplied, "to" is used, and "by" is ignored. 9954 * Optional additional member include "from" (the value the attribute should start animating from, defaults to current value), and "unit" (the units to apply to the values). 9955 * @property attributes 9956 * @type Object 9957 */ 9958 this.attributes = attributes || {}; 9959 9960 /** 9961 * The length of the animation. Defaults to "1" (second). 9962 * @property duration 9963 * @type Number 9964 */ 9965 this.duration = !YAHOO.lang.isUndefined(duration) ? duration : 1; 9966 9967 /** 9968 * The method that will provide values to the attribute(s) during the animation. 9969 * Defaults to "YAHOO.util.Easing.easeNone". 9970 * @property method 9971 * @type Function 9972 */ 9973 this.method = method || Y.Easing.easeNone; 9974 9975 /** 9976 * Whether or not the duration should be treated as seconds. 9977 * Defaults to true. 9978 * @property useSeconds 9979 * @type Boolean 9980 */ 9981 this.useSeconds = true; // default to seconds 9982 9983 /** 9984 * The location of the current animation on the timeline. 9985 * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. 9986 * @property currentFrame 9987 * @type Int 9988 */ 9989 this.currentFrame = 0; 9990 9991 /** 9992 * The total number of frames to be executed. 9993 * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. 9994 * @property totalFrames 9995 * @type Int 9996 */ 9997 this.totalFrames = Y.AnimMgr.fps; 9998 9999 /** 10000 * Changes the animated element 10001 * @method setEl 10002 */ 10003 this.setEl = function(element) { 10004 el = Y.Dom.get(element); 10005 }; 10006 10007 /** 10008 * Returns a reference to the animated element. 10009 * @method getEl 10010 * @return {HTMLElement} 10011 */ 10012 this.getEl = function() { return el; }; 10013 10014 /** 10015 * Checks whether the element is currently animated. 10016 * @method isAnimated 10017 * @return {Boolean} current value of isAnimated. 10018 */ 10019 this.isAnimated = function() { 10020 return isAnimated; 10021 }; 10022 10023 /** 10024 * Returns the animation start time. 10025 * @method getStartTime 10026 * @return {Date} current value of startTime. 10027 */
10028 this.getStartTime = function() { 10029 return startTime; 10030 }; 10031 10032 this.runtimeAttributes = {}; 10033 10034 var logger = {}; 10035 logger.log = function() {YAHOO.log.apply(window, arguments)}; 10036 10037 logger.log('creating new instance of ' + this); 10038 10039 /** 10040 * Starts the animation by registering it with the animation manager. 10041 * @method animate 10042 */ 10043 this.animate = function() { 10044 if ( this.isAnimated() ) { 10045 return false; 10046 } 10047 10048 this.currentFrame = 0; 10049 10050 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Y.AnimMgr.fps * this.duration) : this.duration; 10051 10052 if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration 10053 this.totalFrames = 1; 10054 } 10055 Y.AnimMgr.registerElement(this); 10056 return true; 10057 }; 10058 10059 /** 10060 * Stops the animation. Normally called by AnimMgr when animation completes. 10061 * @method stop 10062 * @param {Boolean} finish (optional) If true, animation will jump to final frame. 10063 */ 10064 this.stop = function(finish) { 10065 if (!this.isAnimated()) { // nothing to stop 10066 return false; 10067 } 10068 10069 if (finish) { 10070 this.currentFrame = this.totalFrames; 10071 this._onTween.fire(); 10072 } 10073 Y.AnimMgr.stop(this); 10074 }; 10075 10076 this._handleStart = function() { 10077 this.onStart.fire(); 10078 10079 this.runtimeAttributes = {}; 10080 for (var attr in this.attributes) { 10081 if (this.attributes.hasOwnProperty(attr)) { 10082 this.setRuntimeAttribute(attr); 10083 } 10084 } 10085 10086 isAnimated = true; 10087 actualFrames = 0; 10088 startTime = new Date(); 10089 }; 10090 10091 /** 10092 * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s). 10093 * @private 10094 */ 10095 10096 this._handleTween = function() { 10097 var data = { 10098 duration: new Date() - this.getStartTime(), 10099 currentFrame: this.currentFrame 10100 }; 10101 10102 data.toString = function() { 10103 return ( 10104 'duration: ' + data.duration + 10105 ', currentFrame: ' + data.currentFrame 10106 ); 10107 }; 10108 10109 this.onTween.fire(data); 10110 10111 var runtimeAttributes = this.runtimeAttributes; 10112 10113 for (var attr in runtimeAttributes) { 10114 if (runtimeAttributes.hasOwnProperty(attr)) { 10115 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); 10116 } 10117 } 10118 10119 this.afterTween.fire(data); 10120 10121 actualFrames += 1; 10122 }; 10123 10124 this._handleComplete = function() { 10125 var actual_duration = (new Date() - startTime) / 1000 ; 10126 10127 var data = { 10128 duration: actual_duration, 10129 frames: actualFrames, 10130 fps: actualFrames / actual_duration 10131 }; 10132 10133 data.toString = function() { 10134 return ( 10135 'duration: ' + data.duration + 10136 ', frames: ' + data.frames + 10137 ', fps: ' + data.fps 10138 ); 10139 }; 10140 10141 isAnimated = false; 10142 actualFrames = 0; 10143 this.onComplete.fire(data); 10144 }; 10145 10146 /** 10147 * Custom event that fires after onStart, useful in subclassing 10148 * @private 10149 */ 10150 this._onStart = new Y.CustomEvent('_start', this, true); 10151 10152 /** 10153 * Custom event that fires when animation begins 10154 * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction) 10155 * @event onStart 10156 */ 10157 this.onStart = new Y.CustomEvent('start', this); 10158 10159 /** 10160 * Custom event that fires between each frame 10161 * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction) 10162 * @event onTween 10163 */ 10164 this.onTween = new Y.CustomEvent('tween', this); 10165 10166 /** 10167 * Custom event that fires between each frame 10168 * Listen via subscribe method (e.g. myAnim.afterTween.subscribe(someFunction) 10169 * @event afterTween 10170 */ 10171 this.afterTween = new Y.CustomEvent('afterTween', this); 10172 10173 /** 10174 * Custom event that fires after onTween 10175 * @private 10176 */
10177 this._onTween = new Y.CustomEvent('_tween', this, true); 10178 10179 /** 10180 * Custom event that fires when animation ends 10181 * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction) 10182 * @event onComplete 10183 */ 10184 this.onComplete = new Y.CustomEvent('complete', this); 10185 /** 10186 * Custom event that fires after onComplete 10187 * @private 10188 */ 10189 this._onComplete = new Y.CustomEvent('_complete', this, true); 10190 10191 this._onStart.subscribe(this._handleStart); 10192 this._onTween.subscribe(this._handleTween); 10193 this._onComplete.subscribe(this._handleComplete); 10194 } 10195}; 10196 10197 Y.Anim = Anim; 10198})(); 10199/** 10200 * Handles animation queueing and threading. 10201 * Used by Anim and subclasses. 10202 * @class AnimMgr 10203 * @namespace YAHOO.util 10204 */ 10205YAHOO.util.AnimMgr = new function() { 10206 /** 10207 * Reference to the animation Interval. 10208 * @property thread 10209 * @private 10210 * @type Int 10211 */ 10212 var thread = null; 10213 10214 /** 10215 * The current queue of registered animation objects. 10216 * @property queue 10217 * @private 10218 * @type Array 10219 */ 10220 var queue = []; 10221 10222 /** 10223 * The number of active animations. 10224 * @property tweenCount 10225 * @private 10226 * @type Int 10227 */ 10228 var tweenCount = 0; 10229 10230 /** 10231 * Base frame rate (frames per second). 10232 * Arbitrarily high for better x-browser calibration (slower browsers drop more frames). 10233 * @property fps 10234 * @type Int 10235 * 10236 */ 10237 this.fps = 1000; 10238 10239 /** 10240 * Interval delay in milliseconds, defaults to fastest possible. 10241 * @property delay 10242 * @type Int 10243 * 10244 */ 10245 this.delay = 20; 10246 10247 /** 10248 * Adds an animation instance to the animation queue. 10249 * All animation instances must be registered in order to animate. 10250 * @method registerElement 10251 * @param {object} tween The Anim instance to be be registered 10252 */ 10253 this.registerElement = function(tween) { 10254 queue[queue.length] = tween; 10255 tweenCount += 1; 10256 tween._onStart.fire(); 10257 this.start(); 10258 }; 10259 10260 var _unregisterQueue = []; 10261 var _unregistering = false; 10262 10263 var doUnregister = function() { 10264 var next_args = _unregisterQueue.shift(); 10265 unRegister.apply(YAHOO.util.AnimMgr,next_args); 10266 if (_unregisterQueue.length) { 10267 arguments.callee(); 10268 } 10269 }; 10270 10271 var unRegister = function(tween, index) { 10272 index = index || getIndex(tween); 10273 if (!tween.isAnimated() || index === -1) { 10274 return false; 10275 } 10276 10277 tween._onComplete.fire(); 10278 queue.splice(index, 1); 10279 10280 tweenCount -= 1; 10281 if (tweenCount <= 0) { 10282 this.stop(); 10283 } 10284 10285 return true; 10286 }; 10287 10288 /** 10289 * removes an animation instance from the animation queue. 10290 * All animation instances must be registered in order to animate. 10291 * @method unRegister 10292 * @param {object} tween The Anim instance to be be registered 10293 * @param {Int} index The index of the Anim instance 10294 * @private 10295 */ 10296 this.unRegister = function() { 10297 _unregisterQueue.push(arguments); 10298 if (!_unregistering) { 10299 _unregistering = true; 10300 doUnregister(); 10301 _unregistering = false; 10302 } 10303 } 10304 10305 /** 10306 * Starts the animation thread. 10307 * Only one thread can run at a time. 10308 * @method start 10309 */ 10310 this.start = function() { 10311 if (thread === null) { 10312 thread = setInterval(this.run, this.delay); 10313 } 10314 }; 10315 10316 /** 10317 * Stops the animation thread or a specific animation instance. 10318 * @method stop 10319 * @param {object} tween A specific Anim instance to stop (optional) 10320 * If no instance given, Manager stops thread and all animations. 10321 */ 10322 this.stop = function(tween) { 10323 if (!tween) { 10324 clearInterval(thread); 10325 10326 for (var i = 0, len = queue.length; i < len; ++i) { 10327 this.unRegister(queue[0], 0); 10328 } 10329 10330 queue = []; 10331 thread = null; 10332 tweenCount = 0; 10333 } 10334 else { 10335 this.unRegister(tween); 10336 } 10337 }; 10338 10339 /** 10340 * Called per Interval to handle each animation frame. 10341 * @method run 10342 */ 10343 this.run = function() { 10344 for (var i = 0, len = queue.length; i < len; ++i) { 10345 var tween = queue[i]; 10346 if ( !tween || !tween.isAnimated() ) { continue; } 10347 10348 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) 10349 { 10350 tween.currentFrame += 1; 10351
10352 if (tween.useSeconds) { 10353 correctFrame(tween); 10354 } 10355 tween._onTween.fire(); 10356 } 10357 else { YAHOO.util.AnimMgr.stop(tween, i); } 10358 } 10359 }; 10360 10361 var getIndex = function(anim) { 10362 for (var i = 0, len = queue.length; i < len; ++i) { 10363 if (queue[i] === anim) { 10364 return i; // note return; 10365 } 10366 } 10367 return -1; 10368 }; 10369 10370 /** 10371 * On the fly frame correction to keep animation on time. 10372 * @method correctFrame 10373 * @private 10374 * @param {Object} tween The Anim instance being corrected. 10375 */ 10376 var correctFrame = function(tween) { 10377 var frames = tween.totalFrames; 10378 var frame = tween.currentFrame; 10379 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); 10380 var elapsed = (new Date() - tween.getStartTime()); 10381 var tweak = 0; 10382 10383 if (elapsed < tween.duration * 1000) { // check if falling behind 10384 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); 10385 } else { // went over duration, so jump to end 10386 tweak = frames - (frame + 1); 10387 } 10388 if (tweak > 0 && isFinite(tweak)) { // adjust if needed 10389 if (tween.currentFrame + tweak >= frames) {// dont go past last frame 10390 tweak = frames - (frame + 1); 10391 } 10392 10393 tween.currentFrame += tweak; 10394 } 10395 }; 10396 this._queue = queue; 10397 this._getIndex = getIndex; 10398}; 10399/** 10400 * Used to calculate Bezier splines for any number of control points. 10401 * @class Bezier 10402 * @namespace YAHOO.util 10403 * 10404 */ 10405YAHOO.util.Bezier = new function() { 10406 /** 10407 * Get the current position of the animated element based on t. 10408 * Each point is an array of "x" and "y" values (0 = x, 1 = y) 10409 * At least 2 points are required (start and end). 10410 * First point is start. Last point is end. 10411 * Additional control points are optional. 10412 * @method getPosition 10413 * @param {Array} points An array containing Bezier points 10414 * @param {Number} t A number between 0 and 1 which is the basis for determining current position 10415 * @return {Array} An array containing int x and y member data 10416 */ 10417 this.getPosition = function(points, t) { 10418 var n = points.length; 10419 var tmp = []; 10420 10421 for (var i = 0; i < n; ++i){ 10422 tmp[i] = [points[i][0], points[i][1]]; // save input 10423 } 10424 10425 for (var j = 1; j < n; ++j) { 10426 for (i = 0; i < n - j; ++i) { 10427 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; 10428 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; 10429 } 10430 } 10431 10432 return [ tmp[0][0], tmp[0][1] ]; 10433 10434 }; 10435}; 10436(function() { 10437/** 10438 * Anim subclass for color transitions. 10439 * <p>Usage: <code>var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);</code> Color values can be specified with either 112233, #112233, 10440 * [255,255,255], or rgb(255,255,255)</p> 10441 * @class ColorAnim 10442 * @namespace YAHOO.util 10443 * @requires YAHOO.util.Anim 10444 * @requires YAHOO.util.AnimMgr 10445 * @requires YAHOO.util.Easing 10446 * @requires YAHOO.util.Bezier 10447 * @requires YAHOO.util.Dom 10448 * @requires YAHOO.util.Event 10449 * @constructor 10450 * @extends YAHOO.util.Anim 10451 * @param {HTMLElement | String} el Reference to the element that will be animated 10452 * @param {Object} attributes The attribute(s) to be animated. 10453 * Each attribute is an object with at minimum a "to" or "by" member defined. 10454 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). 10455 * All attribute names use camelCase. 10456 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based 10457 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method) 10458 */ 10459 var ColorAnim = function(el, attributes, duration, method) { 10460 ColorAnim.superclass.constructor.call(this, el, attributes, duration, method); 10461 }; 10462 10463 ColorAnim.NAME = 'ColorAnim'; 10464 10465 ColorAnim.DEFAULT_BGCOLOR = '#fff'; 10466 // shorthand 10467 var Y = YAHOO.util; 10468 YAHOO.extend(ColorAnim, Y.Anim); 10469 10470 var superclass = ColorAnim.superclass; 10471 var proto = ColorAnim.prototype; 10472 10473 proto.patterns.color = /color$/i; 10474 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i; 10475 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i; 10476 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i; 10477 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari 10478 10479 /** 10480 * Attempts to parse the given string and return a 3-tuple. 10481 * @method parseColor 10482 * @param {String} s The string to parse. 10483 * @return {Array} The 3-tuple of rgb values. 10484 */ 10485 proto.parseColor = function(s) { 10486 if (s.length == 3) { return s; } 10487 10488 var c = this.patterns.hex.exec(s); 10489 if (c && c.length == 4) { 10490 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ]; 10491 } 10492 10493 c = this.patterns.rgb.exec(s); 10494 if (c && c.length == 4) { 10495 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ]; 10496 } 10497 10498 c = this.patterns.hex3.exec(s); 10499 if (c && c.length == 4) { 10500 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ]; 10501 } 10502 10503 return null; 10504 }; 10505 10506 proto.getAttribute = function(attr) { 10507 var el = this.getEl(); 10508 if (this.patterns.color.test(attr) ) { 10509 var val = YAHOO.util.Dom.getStyle(el, attr); 10510 10511 var that = this; 10512 if (this.patterns.transparent.test(val)) { // bgcolor default 10513 var parent = YAHOO.util.Dom.getAncestorBy(el, function(node) { 10514 return !that.patterns.transparent.test(val); 10515 }); 10516 10517 if (parent) { 10518 val = Y.Dom.getStyle(parent, attr); 10519 } else { 10520 val = ColorAnim.DEFAULT_BGCOLOR; 10521 } 10522 } 10523 } else { 10524 val = superclass.getAttribute.call(this, attr); 10525 } 10526 10527 return val; 10528 }; 10529 10530 proto.doMethod = function(attr, start, end) { 10531 var val; 10532 10533 if ( this.patterns.color.test(attr) ) { 10534 val = []; 10535 for (var i = 0, len = start.length; i < len; ++i) { 10536 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]); 10537 } 10538 10539 val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')'; 10540 } 10541 else { 10542 val = superclass.doMethod.call(this, attr, start, end); 10543 } 10544 10545 return val; 10546 }; 10547 10548 proto.setRuntimeAttribute = function(attr) { 10549 superclass.setRuntimeAttribute.call(this, attr); 10550 10551 if ( this.patterns.color.test(attr) ) { 10552 var attributes = this.attributes; 10553 var start = this.parseColor(this.runtimeAttributes[attr].start); 10554 var end = this.parseColor(this.runtimeAttributes[attr].end); 10555 // fix colors if going "by" 10556 if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) { 10557 end = this.parseColor(attributes[attr].by); 10558 10559 for (var i = 0, len = start.length; i < len; ++i) { 10560 end[i] = start[i] + end[i]; 10561 } 10562 } 10563 10564 this.runtimeAttributes[attr].start = start; 10565 this.runtimeAttributes[attr].end = end; 10566 } 10567 }; 10568 10569 Y.ColorAnim = ColorAnim; 10570})(); 10571/*!
10572TERMS OF USE - EASING EQUATIONS 10573Open source under the BSD License. 10574Copyright 2001 Robert Penner All rights reserved. 10575 10576Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 10577 10578 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10579 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10580 * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10581 10582THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10583*/ 10584 10585/** 10586 * Singleton that determines how an animation proceeds from start to end. 10587 * @class Easing 10588 * @namespace YAHOO.util 10589*/ 10590 10591YAHOO.util.Easing = { 10592 10593 /** 10594 * Uniform speed between points. 10595 * @method easeNone 10596 * @param {Number} t Time value used to compute current value 10597 * @param {Number} b Starting value 10598 * @param {Number} c Delta between start and end values 10599 * @param {Number} d Total length of animation 10600 * @return {Number} The computed value for the current animation frame 10601 */ 10602 easeNone: function (t, b, c, d) { 10603 return c*t/d + b; 10604 }, 10605 10606 /** 10607 * Begins slowly and accelerates towards end. 10608 * @method easeIn 10609 * @param {Number} t Time value used to compute current value 10610 * @param {Number} b Starting value 10611 * @param {Number} c Delta between start and end values 10612 * @param {Number} d Total length of animation 10613 * @return {Number} The computed value for the current animation frame 10614 */ 10615 easeIn: function (t, b, c, d) { 10616 return c*(t/=d)*t + b; 10617 }, 10618 10619 /** 10620 * Begins quickly and decelerates towards end. 10621 * @method easeOut 10622 * @param {Number} t Time value used to compute current value 10623 * @param {Number} b Starting value 10624 * @param {Number} c Delta between start and end values 10625 * @param {Number} d Total length of animation 10626 * @return {Number} The computed value for the current animation frame 10627 */ 10628 easeOut: function (t, b, c, d) { 10629 return -c *(t/=d)*(t-2) + b; 10630 }, 10631 10632 /** 10633 * Begins slowly and decelerates towards end. 10634 * @method easeBoth 10635 * @param {Number} t Time value used to compute current value 10636 * @param {Number} b Starting value 10637 * @param {Number} c Delta between start and end values 10638 * @param {Number} d Total length of animation 10639 * @return {Number} The computed value for the current animation frame 10640 */ 10641 easeBoth: function (t, b, c, d) { 10642 if ((t/=d/2) < 1) { 10643 return c/2*t*t + b; 10644 } 10645 10646 return -c/2 * ((--t)*(t-2) - 1) + b; 10647 }, 10648 10649 /** 10650 * Begins slowly and accelerates towards end. 10651 * @method easeInStrong 10652 * @param {Number} t Time value used to compute current value 10653 * @param {Number} b Starting value 10654 * @param {Number} c Delta between start and end values 10655 * @param {Number} d Total length of animation 10656 * @return {Number} The computed value for the current animation frame 10657 */ 10658 easeInStrong: function (t, b, c, d) { 10659 return c*(t/=d)*t*t*t + b; 10660 }, 10661 10662 /** 10663 * Begins quickly and decelerates towards end. 10664 * @method easeOutStrong 10665 * @param {Number} t Time value used to compute current value 10666 * @param {Number} b Starting value 10667 * @param {Number} c Delta between start and end values 10668 * @param {Number} d Total length of animation 10669 * @return {Number} The computed value for the current animation frame 10670 */ 10671 easeOutStrong: function (t, b, c, d) { 10672 return -c * ((t=t/d-1)*t*t*t - 1) + b; 10673 }, 10674 10675 /** 10676 * Begins slowly and decelerates towards end. 10677 * @method easeBothStrong 10678 * @param {Number} t Time value used to compute current value 10679 * @param {Number} b Starting value 10680 * @param {Number} c Delta between start and end values 10681 * @param {Number} d Total length of animation 10682 * @return {Number} The computed value for the current animation frame 10683 */ 10684 easeBothStrong: function (t, b, c, d) { 10685 if ((t/=d/2) < 1) { 10686 return c/2*t*t*t*t + b; 10687 } 10688 10689 return -c/2 * ((t-=2)*t*t*t - 2) + b; 10690 }, 10691 10692 /** 10693 * Snap in elastic effect. 10694 * @method elasticIn 10695 * @param {Number} t Time value used to compute current value 10696 * @param {Number} b Starting value 10697 * @param {Number} c Delta between start and end values 10698 * @param {Number} d Total length of animation 10699 * @param {Number} a Amplitude (optional) 10700 * @param {Number} p Period (optional) 10701 * @return {Number} The computed value for the current animation frame 10702 */ 10703 10704 elasticIn: function (t, b, c, d, a, p) { 10705 if (t == 0) { 10706 return b; 10707 } 10708 if ( (t /= d) == 1 ) { 10709 return b+c; 10710 } 10711 if (!p) { 10712 p=d*.3; 10713 } 10714 10715 if (!a || a < Math.abs(c)) { 10716 a = c; 10717 var s = p/4; 10718 } 10719 else { 10720 var s = p/(2*Math.PI) * Math.asin (c/a); 10721 } 10722 10723 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 10724 }, 10725 10726 /** 10727 * Snap out elastic effect. 10728 * @method elasticOut 10729 * @param {Number} t Time value used to compute current value 10730 * @param {Number} b Starting value 10731 * @param {Number} c Delta between start and end values 10732 * @param {Number} d Total length of animation 10733 * @param {Number} a Amplitude (optional) 10734 * @param {Number} p Period (optional) 10735 * @return {Number} The computed value for the current animation frame 10736 */ 10737 elasticOut: function (t, b, c, d, a, p) { 10738 if (t == 0) { 10739 return b; 10740 } 10741 if ( (t /= d) == 1 ) { 10742 return b+c; 10743 } 10744 if (!p) { 10745 p=d*.3; 10746 } 10747 10748 if (!a || a < Math.abs(c)) { 10749 a = c; 10750 var s = p / 4; 10751 } 10752 else { 10753 var s = p/(2*Math.PI) * Math.asin (c/a); 10754 } 10755 10756 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 10757 }, 10758 10759 /** 10760 * Snap both elastic effect. 10761 * @method elasticBoth 10762 * @param {Number} t Time value used to compute current value 10763 * @param {Number} b Starting value 10764 * @param {Number} c Delta between start and end values 10765 * @param {Number} d Total length of animation 10766 * @param {Number} a Amplitude (optional) 10767 * @param {Number} p Period (optional) 10768 * @return {Number} The computed value for the current animation frame 10769 */ 10770 elasticBoth: function (t, b, c, d, a, p) { 10771 if (t == 0) { 10772 return b; 10773 } 10774 10775 if ( (t /= d/2) == 2 ) { 10776 return b+c; 10777 } 10778 10779 if (!p) { 10780 p = d*(.3*1.5); 10781 } 10782 10783 if ( !a || a < Math.abs(c) ) { 10784 a = c; 10785 var s = p/4; 10786 } 10787 else { 10788 var s = p/(2*Math.PI) * Math.asin (c/a); 10789 } 10790 10791 if (t < 1) { 10792 return -.5*(a*Math.pow(2,10*(t-=1)) * 10793 Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 10794 } 10795 return a*Math.pow(2,-10*(t-=1)) * 10796 Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 10797 }, 10798 10799 10800 /** 10801 * Backtracks slightly, then reverses direction and moves to end. 10802 * @method backIn 10803 * @param {Number} t Time value used to compute current value 10804 * @param {Number} b Starting value 10805 * @param {Number} c Delta between start and end values 10806 * @param {Number} d Total length of animation 10807 * @param {Number} s Overshoot (optional) 10808 * @return {Number} The computed value for the current animation frame 10809 */ 10810 backIn: function (t, b, c, d, s) { 10811 if (typeof s == 'undefined') { 10812 s = 1.70158; 10813 } 10814 return c*(t/=d)*t*((s+1)*t - s) + b; 10815 }, 10816 10817 /** 10818 * Overshoots end, then reverses and comes back to end. 10819 * @method backOut 10820 * @param {Number} t Time value used to compute current value 10821 * @param {Number} b Starting value 10822 * @param {Number} c Delta between start and end values 10823 * @param {Number} d Total length of animation 10824 * @param {Number} s Overshoot (optional) 10825 * @return {Number} The computed value for the current animation frame 10826 */ 10827 backOut: function (t, b, c, d, s) { 10828 if (typeof s == 'undefined') { 10829 s = 1.70158; 10830 } 10831 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 10832 }, 10833 10834 /** 10835 * Backtracks slightly, then reverses direction, overshoots end, 10836 * then reverses and comes back to end. 10837 * @method backBoth 10838 * @param {Number} t Time value used to compute current value 10839 * @param {Number} b Starting value 10840 * @param {Number} c Delta between start and end values 10841 * @param {Number} d Total length of animation 10842 * @param {Number} s Overshoot (optional) 10843 * @return {Number} The computed value for the current animation frame 10844 */ 10845 backBoth: function (t, b, c, d, s) { 10846 if (typeof s == 'undefined') { 10847 s = 1.70158; 10848 } 10849 10850 if ((t /= d/2 ) < 1) { 10851 return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 10852 } 10853 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 10854 }, 10855 10856 /** 10857 * Bounce off of start. 10858 * @method bounceIn 10859 * @param {Number} t Time value used to compute current value 10860 * @param {Number} b Starting value 10861 * @param {Number} c Delta between start and end values 10862 * @param {Number} d Total length of animation 10863 * @return {Number} The computed value for the current animation frame 10864 */ 10865 bounceIn: function (t, b, c, d) { 10866 return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b; 10867 }, 10868 10869 /** 10870 * Bounces off end. 10871 * @method bounceOut 10872 * @param {Number} t Time value used to compute current value 10873 * @param {Number} b Starting value 10874 * @param {Number} c Delta between start and end values 10875 * @param {Number} d Total length of animation 10876 * @return {Number} The computed value for the current animation frame 10877 */ 10878 bounceOut: function (t, b, c, d) { 10879 if ((t/=d) < (1/2.75)) { 10880 return c*(7.5625*t*t) + b; 10881 } else if (t < (2/2.75)) { 10882 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 10883 } else if (t < (2.5/2.75)) { 10884 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 10885 } 10886 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 10887 }, 10888 10889 /** 10890 * Bounces off start and end. 10891 * @method bounceBoth 10892 * @param {Number} t Time value used to compute current value 10893 * @param {Number} b Starting value 10894 * @param {Number} c Delta between start and end values 10895 * @param {Number} d Total length of animation 10896 * @return {Number} The computed value for the current animation frame 10897 */ 10898 bounceBoth: function (t, b, c, d) { 10899 if (t < d/2) { 10900 return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b; 10901 } 10902 return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b; 10903 } 10904}; 10905 10906(function() { 10907/** 10908 * Anim subclass for moving elements along a path defined by the "points" 10909 * member of "attributes". All "points" are arrays with x, y coordinates. 10910 * <p>Usage: <code>var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p> 10911 * @class Motion 10912 * @namespace YAHOO.util 10913 * @requires YAHOO.util.Anim 10914 * @requires YAHOO.util.AnimMgr 10915 * @requires YAHOO.util.Easing 10916 * @requires YAHOO.util.Bezier 10917 * @requires YAHOO.util.Dom 10918 * @requires YAHOO.util.Event 10919 * @requires YAHOO.util.CustomEvent 10920 * @constructor 10921 * @extends YAHOO.util.ColorAnim 10922 * @param {String | HTMLElement} el Reference to the element that will be animated 10923 * @param {Object} attributes The attribute(s) to be animated. 10924 * Each attribute is an object with at minimum a "to" or "by" member defined. 10925 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). 10926 * All attribute names use camelCase. 10927 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based 10928 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method) 10929 */ 10930 var Motion = function(el, attributes, duration, method) { 10931 if (el) { // dont break existing subclasses not using YAHOO.extend 10932 Motion.superclass.constructor.call(this, el, attributes, duration, method); 10933 } 10934 }; 10935 10936 10937 Motion.NAME = 'Motion'; 10938 10939 // shorthand 10940 var Y = YAHOO.util; 10941 YAHOO.extend(Motion, Y.ColorAnim); 10942 10943 var superclass = Motion.superclass; 10944 var proto = Motion.prototype; 10945 10946 proto.patterns.points = /^points$/i; 10947 10948 proto.setAttribute = function(attr, val, unit) { 10949 if ( this.patterns.points.test(attr) ) { 10950 unit = unit || 'px'; 10951 superclass.setAttribute.call(this, 'left', val[0], unit); 10952 superclass.setAttribute.call(this, 'top', val[1], unit); 10953 } else { 10954 superclass.setAttribute.call(this, attr, val, unit); 10955 } 10956 }; 10957 10958 proto.getAttribute = function(attr) { 10959 if ( this.patterns.points.test(attr) ) { 10960 var val = [ 10961 superclass.getAttribute.call(this, 'left'), 10962 superclass.getAttribute.call(this, 'top') 10963 ]; 10964 } else { 10965 val = superclass.getAttribute.call(this, attr); 10966 } 10967 10968 return val; 10969 }; 10970 10971 proto.doMethod = function(attr, start, end) { 10972 var val = null; 10973 10974 if ( this.patterns.points.test(attr) ) { 10975 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100; 10976 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t); 10977 } else { 10978 val = superclass.doMethod.call(this, attr, start, end); 10979 } 10980 return val; 10981 }; 10982 10983 proto.setRuntimeAttribute = function(attr) { 10984 if ( this.patterns.points.test(attr) ) { 10985 var el = this.getEl(); 10986 var attributes = this.attributes; 10987 var start; 10988 var control = attributes['points']['control'] || []; 10989 var end; 10990 var i, len; 10991 10992 if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points 10993 control = [control]; 10994 } else { // break reference to attributes.points.control 10995 var tmp = []; 10996 for (i = 0, len = control.length; i< len; ++i) { 10997 tmp[i] = control[i]; 10998 } 10999 control = tmp; 11000 } 11001 11002 if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative 11003 Y.Dom.setStyle(el, 'position', 'relative'); 11004 } 11005 11006 if ( isset(attributes['points']['from']) ) { 11007 Y.Dom.setXY(el, attributes['points']['from']); // set position to from point 11008 } 11009 else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position 11010 11011 start = this.getAttribute('points'); // get actual top & left 11012 11013 // TO beats BY, per SMIL 2.1 spec 11014 if ( isset(attributes['points']['to']) ) { 11015 end = translateValues.call(this, attributes['points']['to'], start); 11016 11017 var pageXY = Y.Dom.getXY(this.getEl()); 11018 for (i = 0, len = control.length; i < len;
11018 ++i) { 11019 control[i] = translateValues.call(this, control[i], start); 11020 } 11021 11022 11023 } else if ( isset(attributes['points']['by']) ) { 11024 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ]; 11025 11026 for (i = 0, len = control.length; i < len; ++i) { 11027 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ]; 11028 } 11029 } 11030 11031 this.runtimeAttributes[attr] = [start]; 11032 11033 if (control.length > 0) { 11034 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control); 11035 } 11036 11037 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end; 11038 } 11039 else { 11040 superclass.setRuntimeAttribute.call(this, attr); 11041 } 11042 }; 11043 11044 var translateValues = function(val, start) { 11045 var pageXY = Y.Dom.getXY(this.getEl()); 11046 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ]; 11047 11048 return val; 11049 }; 11050 11051 var isset = function(prop) { 11052 return (typeof prop !== 'undefined'); 11053 }; 11054 11055 Y.Motion = Motion; 11056})(); 11057(function() { 11058/** 11059 * Anim subclass for scrolling elements to a position defined by the "scroll" 11060 * member of "attributes". All "scroll" members are arrays with x, y scroll positions. 11061 * <p>Usage: <code>var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p> 11062 * @class Scroll 11063 * @namespace YAHOO.util 11064 * @requires YAHOO.util.Anim 11065 * @requires YAHOO.util.AnimMgr 11066 * @requires YAHOO.util.Easing 11067 * @requires YAHOO.util.Bezier 11068 * @requires YAHOO.util.Dom 11069 * @requires YAHOO.util.Event 11070 * @requires YAHOO.util.CustomEvent 11071 * @extends YAHOO.util.ColorAnim 11072 * @constructor 11073 * @param {String or HTMLElement} el Reference to the element that will be animated 11074 * @param {Object} attributes The attribute(s) to be animated. 11075 * Each attribute is an object with at minimum a "to" or "by" member defined. 11076 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). 11077 * All attribute names use camelCase. 11078 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based 11079 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method) 11080 */ 11081 var Scroll = function(el, attributes, duration, method) { 11082 if (el) { // dont break existing subclasses not using YAHOO.extend 11083 Scroll.superclass.constructor.call(this, el, attributes, duration, method); 11084 } 11085 }; 11086 11087 Scroll.NAME = 'Scroll'; 11088 11089 // shorthand 11090 var Y = YAHOO.util; 11091 YAHOO.extend(Scroll, Y.ColorAnim); 11092 11093 var superclass = Scroll.superclass; 11094 var proto = Scroll.prototype; 11095 11096 proto.doMethod = function(attr, start, end) { 11097 var val = null; 11098 11099 if (attr == 'scroll') { 11100 val = [ 11101 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames), 11102 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames) 11103 ]; 11104 11105 } else { 11106 val = superclass.doMethod.call(this, attr, start, end); 11107 } 11108 return val; 11109 }; 11110 11111 proto.getAttribute = function(attr) { 11112 var val = null; 11113 var el = this.getEl(); 11114 11115 if (attr == 'scroll') { 11116 val = [ el.scrollLeft, el.scrollTop ]; 11117 } else { 11118 val = superclass.getAttribute.call(this, attr); 11119 } 11120 11121 return val; 11122 }; 11123 11124 proto.setAttribute = function(attr, val, unit) { 11125 var el = this.getEl(); 11126 11127 if (attr == 'scroll') { 11128 el.scrollLeft = val[0]; 11129 el.scrollTop = val[1]; 11130 } else { 11131 superclass.setAttribute.call(this, attr, val, unit); 11132 } 11133 }; 11134 11135 Y.Scroll = Scroll; 11136})(); 11137YAHOO.register("animation", YAHOO.util.Anim, {version: "2.9.0", build: "2800"}); 11138/* 11139Copyright (c) 2011, Yahoo! Inc. All rights reserved. 11140Code licensed under the BSD License:
11141http://developer.yahoo.com/yui/license.html 11142version: 2.9.0 11143*/ 11144/****************************************************************************/ 11145/****************************************************************************/ 11146/****************************************************************************/ 11147 11148/** 11149 * The LogMsg class defines a single log message. 11150 * 11151 * @class LogMsg 11152 * @constructor 11153 * @param oConfigs {Object} Object literal of configuration params. 11154 */ 11155YAHOO.widget.LogMsg = function(oConfigs) { 11156 // Parse configs 11157 /** 11158 * Log message. 11159 * 11160 * @property msg 11161 * @type String 11162 */ 11163 this.msg = 11164 /** 11165 * Log timestamp. 11166 * 11167 * @property time 11168 * @type Date 11169 */ 11170 this.time = 11171 11172 /** 11173 * Log category. 11174 * 11175 * @property category 11176 * @type String 11177 */ 11178 this.category = 11179 11180 /** 11181 * Log source. The first word passed in as the source argument. 11182 * 11183 * @property source 11184 * @type String 11185 */ 11186 this.source = 11187 11188 /** 11189 * Log source detail. The remainder of the string passed in as the source argument, not 11190 * including the first word (if any). 11191 * 11192 * @property sourceDetail
11193 * @type String 11194 */ 11195 this.sourceDetail = null; 11196 11197 if (oConfigs && (oConfigs.constructor == Object)) { 11198 for(var param in oConfigs) { 11199 if (oConfigs.hasOwnProperty(param)) { 11200 this[param] = oConfigs[param]; 11201 } 11202 } 11203 } 11204}; 11205/****************************************************************************/ 11206/****************************************************************************/ 11207/****************************************************************************/ 11208 11209/** 11210 * The LogWriter class provides a mechanism to log messages through 11211 * YAHOO.widget.Logger from a named source. 11212 * 11213 * @class LogWriter 11214 * @constructor 11215 * @param sSource {String} Source of LogWriter instance. 11216 */ 11217YAHOO.widget.LogWriter = function(sSource) { 11218 if(!sSource) { 11219 YAHOO.log("Could not instantiate LogWriter due to invalid source.", 11220 "error", "LogWriter"); 11221 return; 11222 } 11223 this._source = sSource; 11224 }; 11225 11226///////////////////////////////////////////////////////////////////////////// 11227// 11228// Public methods 11229// 11230///////////////////////////////////////////////////////////////////////////// 11231 11232 /** 11233 * Public accessor to the unique name of the LogWriter instance. 11234 * 11235 * @method toString 11236 * @return {String} Unique name of the LogWriter instance. 11237 */ 11238YAHOO.widget.LogWriter.prototype.toString = function() { 11239 return "LogWriter " + this._sSource; 11240}; 11241 11242/** 11243 * Logs a message attached to the source of the LogWriter. 11244 * Note: the LogReader adds the message and category to the DOM as HTML. 11245 * 11246 * @method log 11247 * @param sMsg {HTML} The log message. 11248 * @param sCategory {HTML} Category name. 11249 */ 11250YAHOO.widget.LogWriter.prototype.log = function(sMsg, sCategory) { 11251 YAHOO.widget.Logger.log(sMsg, sCategory, this._source); 11252}; 11253 11254/** 11255 * Public accessor to get the source name. 11256 * 11257 * @method getSource 11258 * @return {String} The LogWriter source. 11259 */ 11260YAHOO.widget.LogWriter.prototype.getSource = function() { 11261 return this._source; 11262}; 11263 11264/** 11265 * Public accessor to set the source name. 11266 * 11267 * @method setSource 11268 * @param sSource {String} Source of LogWriter instance. 11269 */ 11270YAHOO.widget.LogWriter.prototype.setSource = function(sSource) { 11271 if(!sSource) { 11272 YAHOO.log("Could not set source due to invalid source.", "error", this.toString()); 11273 return; 11274 } 11275 else { 11276 this._source = sSource; 11277 } 11278}; 11279 11280///////////////////////////////////////////////////////////////////////////// 11281// 11282// Private member variables 11283// 11284///////////////////////////////////////////////////////////////////////////// 11285 11286/** 11287 * Source of the LogWriter instance. 11288 * 11289 * @property _source 11290 * @type String 11291 * @private 11292 */ 11293YAHOO.widget.LogWriter.prototype._source = null; 11294 11295 11296 11297 /** 11298 * The Logger widget provides a simple way to read or write log messages in 11299 * JavaScript code. Integration with the YUI Library's debug builds allow 11300 * implementers to access under-the-hood events, errors, and debugging messages. 11301 * Output may be read through a LogReader console and/or output to a browser 11302 * console. 11303 * 11304 * @module logger 11305 * @requires yahoo, event, dom 11306 * @optional dragdrop 11307 * @namespace YAHOO.widget 11308 * @title Logger Widget 11309 */ 11310 11311/****************************************************************************/ 11312/****************************************************************************/ 11313/****************************************************************************/ 11314 11315// Define once 11316if(!YAHOO.widget.Logger) { 11317 /** 11318 * The singleton Logger class provides core log management functionality. Saves 11319 * logs written through the global YAHOO.log function or written by a LogWriter 11320 * instance. Provides access to logs for reading by a LogReader instance or 11321 * native browser console such as the Firebug extension to Firefox or Safari's 11322 * JavaScript console through integration with the console.log() method. 11323 * 11324 * @class Logger 11325 * @static 11326 */ 11327 YAHOO.widget.Logger = { 11328 // Initialize properties 11329 loggerEnabled: true, 11330 _browserConsoleEnabled: false, 11331 categories: ["info","warn","error","time","window"], 11332 sources: ["global"], 11333 _stack: [], // holds all log msgs 11334 maxStackEntries: 2500, 11335 _startTime: new Date().getTime(), // static start timestamp 11336 _lastTime: null, // timestamp of last logged message 11337 _windowErrorsHandled: false, 11338 _origOnWindowError: null 11339 }; 11340
11341 ///////////////////////////////////////////////////////////////////////////// 11342 // 11343 // Public properties 11344 // 11345 ///////////////////////////////////////////////////////////////////////////// 11346 /** 11347 * True if Logger is enabled, false otherwise. 11348 * 11349 * @property loggerEnabled 11350 * @type Boolean 11351 * @static 11352 * @default true 11353 */ 11354 11355 /** 11356 * Array of categories. 11357 * 11358 * @property categories 11359 * @type String[] 11360 * @static 11361 * @default ["info","warn","error","time","window"] 11362 */ 11363 11364 /** 11365 * Array of sources. 11366 * 11367 * @property sources 11368 * @type String[] 11369 * @static 11370 * @default ["global"] 11371 */ 11372 11373 /** 11374 * Upper limit on size of internal stack. 11375 * 11376 * @property maxStackEntries 11377 * @type Number 11378 * @static 11379 * @default 2500 11380 */ 11381 11382 ///////////////////////////////////////////////////////////////////////////// 11383 // 11384 // Private properties 11385 // 11386 ///////////////////////////////////////////////////////////////////////////// 11387 /** 11388 * Internal property to track whether output to browser console is enabled. 11389 * 11390 * @property _browserConsoleEnabled 11391 * @type Boolean 11392 * @static 11393 * @default false 11394 * @private 11395 */ 11396 11397 /** 11398 * Array to hold all log messages. 11399 * 11400 * @property _stack 11401 * @type Array 11402 * @static 11403 * @private 11404 */ 11405 /** 11406 * Static timestamp of Logger initialization. 11407 * 11408 * @property _startTime 11409 * @type Date 11410 * @static 11411 * @private 11412 */ 11413 /** 11414 * Timestamp of last logged message. 11415 * 11416 * @property _lastTime 11417 * @type Date 11418 * @static 11419 * @private 11420 */ 11421 ///////////////////////////////////////////////////////////////////////////// 11422 // 11423 // Public methods 11424 // 11425 ///////////////////////////////////////////////////////////////////////////// 11426 /** 11427 * Saves a log message to the stack and fires newLogEvent. If the log message is 11428 * assigned to an unknown category, creates a new category. If the log message is 11429 * from an unknown source, creates a new source. If browser console is enabled, 11430 * outputs the log message to browser console. 11431 * Note: the LogReader adds the message, category, and source to the DOM 11432 * as HTML. 11433 * 11434 * @method log 11435 * @param sMsg {HTML} The log message. 11436 * @param sCategory {HTML} Category of log message, or null. 11437 * @param sSource {HTML} Source of LogWriter, or null if global. 11438 */ 11439 YAHOO.widget.Logger.log = function(sMsg, sCategory, sSource) { 11440 if(this.loggerEnabled) { 11441 if(!sCategory) { 11442 sCategory = "info"; // default category 11443 } 11444 else { 11445 sCategory = sCategory.toLocaleLowerCase(); 11446 if(this._isNewCategory(sCategory)) { 11447 this._createNewCategory(sCategory); 11448 } 11449 } 11450 var sClass = "global"; // default source 11451 var sDetail = null; 11452 if(sSource) { 11453 var spaceIndex = sSource.indexOf(" "); 11454 if(spaceIndex > 0) { 11455 // Substring until first space 11456 sClass = sSource.substring(0,spaceIndex); 11457 // The rest of the source 11458 sDetail = sSource.substring(spaceIndex,sSource.length); 11459 } 11460 else { 11461 sClass = sSource; 11462 } 11463 if(this._isNewSource(sClass)) { 11464 this._createNewSource(sClass); 11465 } 11466 } 11467 11468 var timestamp = new Date(); 11469 var logEntry = new YAHOO.widget.LogMsg({ 11470 msg: sMsg, 11471 time: timestamp, 11472 category: sCategory, 11473 source: sClass, 11474 sourceDetail: sDetail 11475 }); 11476 11477 var stack = this._stack; 11478 var maxStackEntries = this.maxStackEntries; 11479 if(maxStackEntries && !isNaN(maxStackEntries) && 11480 (stack.length >= maxStackEntries)) { 11481 stack.shift(); 11482 }
11483 stack.push(logEntry); 11484 this.newLogEvent.fire(logEntry); 11485 11486 if(this._browserConsoleEnabled) { 11487 this._printToBrowserConsole(logEntry); 11488 } 11489 return true; 11490 } 11491 else { 11492 return false; 11493 } 11494 }; 11495 11496 /** 11497 * Resets internal stack and startTime, enables Logger, and fires logResetEvent. 11498 * 11499 * @method reset 11500 */ 11501 YAHOO.widget.Logger.reset = function() { 11502 this._stack = []; 11503 this._startTime = new Date().getTime(); 11504 this.loggerEnabled = true; 11505 this.log("Logger reset"); 11506 this.logResetEvent.fire(); 11507 }; 11508 11509 /** 11510 * Public accessor to internal stack of log message objects. 11511 * 11512 * @method getStack 11513 * @return {Object[]} Array of log message objects. 11514 */ 11515 YAHOO.widget.Logger.getStack = function() { 11516 return this._stack; 11517 }; 11518 11519 /** 11520 * Public accessor to internal start time. 11521 * 11522 * @method getStartTime 11523 * @return {Date} Internal date of when Logger singleton was initialized. 11524 */ 11525 YAHOO.widget.Logger.getStartTime = function() { 11526 return this._startTime; 11527 }; 11528 11529 /** 11530 * Disables output to the browser's global console.log() function, which is used 11531 * by the Firebug extension to Firefox as well as Safari. 11532 * 11533 * @method disableBrowserConsole 11534 */ 11535 YAHOO.widget.Logger.disableBrowserConsole = function() { 11536 YAHOO.log("Logger output to the function console.log() has been disabled."); 11537 this._browserConsoleEnabled = false; 11538 }; 11539 11540 /** 11541 * Enables output to the browser's global console.log() function, which is used 11542 * by the Firebug extension to Firefox as well as Safari. 11543 * 11544 * @method enableBrowserConsole 11545 */ 11546 YAHOO.widget.Logger.enableBrowserConsole = function() { 11547 this._browserConsoleEnabled = true; 11548 YAHOO.log("Logger output to the function console.log() has been enabled."); 11549 }; 11550 11551 /** 11552 * Surpresses native JavaScript errors and outputs to console. By default, 11553 * Logger does not handle JavaScript window error events. 11554 * NB: Not all browsers support the window.onerror event. 11555 * 11556 * @method handleWindowErrors 11557 */ 11558 YAHOO.widget.Logger.handleWindowErrors = function() { 11559 if(!YAHOO.widget.Logger._windowErrorsHandled) { 11560 // Save any previously defined handler to call 11561 if(window.error) { 11562 YAHOO.widget.Logger._origOnWindowError = window.onerror; 11563 } 11564 window.onerror = YAHOO.widget.Logger._onWindowError; 11565 YAHOO.widget.Logger._windowErrorsHandled = true; 11566 YAHOO.log("Logger handling of window.onerror has been enabled."); 11567 } 11568 else { 11569 YAHOO.log("Logger handling of window.onerror had already been enabled."); 11570 } 11571 }; 11572 11573 /** 11574 * Unsurpresses native JavaScript errors. By default, 11575 * Logger does not handle JavaScript window error events. 11576 * NB: Not all browsers support the window.onerror event. 11577 * 11578 * @method unhandleWindowErrors 11579 */ 11580 YAHOO.widget.Logger.unhandleWindowErrors = function() { 11581 if(YAHOO.widget.Logger._windowErrorsHandled) { 11582 // Revert to any previously defined handler to call 11583 if(YAHOO.widget.Logger._origOnWindowError) { 11584 window.onerror = YAHOO.widget.Logger._origOnWindowError; 11585 YAHOO.widget.Logger._origOnWindowError = null; 11586 } 11587 else { 11588 window.onerror = null; 11589 } 11590 YAHOO.widget.Logger._windowErrorsHandled = false; 11591 YAHOO.log("Logger handling of window.onerror has been disabled."); 11592 } 11593 else { 11594 YAHOO.log("Logger handling of window.onerror had already been disabled."); 11595 } 11596 }; 11597 11598 ///////////////////////////////////////////////////////////////////////////// 11599 // 11600 // Public events 11601 // 11602 ///////////////////////////////////////////////////////////////////////////// 11603 11604 /** 11605 * Fired when a new category has been created. 11606 * 11607 * @event categoryCreateEvent 11608 * @param sCategory {String} Category name. 11609 */ 11610 YAHOO.widget.Logger.categoryCreateEvent = 11611 new YAHOO.util.CustomEvent("categoryCreate", this, true); 11612 11613 /** 11614 * Fired when a new source has been named. 11615 * 11616 * @event sourceCreateEvent 11617 * @param sSource {String} Source name. 11618 */ 11619 YAHOO.widget.Logger.sourceCreateEvent = 11620 new YAHOO.util.CustomEvent("sourceCreate", this, true); 11621 11622 /** 11623 * Fired when a new log message has been created. 11624 * 11625 * @event newLogEvent
11626 * @param sMsg {String} Log message. 11627 */ 11628 YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true); 11629 11630 /** 11631 * Fired when the Logger has been reset has been created. 11632 * 11633 * @event logResetEvent 11634 */ 11635 YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true); 11636 11637 ///////////////////////////////////////////////////////////////////////////// 11638 // 11639 // Private methods 11640 // 11641 ///////////////////////////////////////////////////////////////////////////// 11642 11643 /** 11644 * Creates a new category of log messages and fires categoryCreateEvent. 11645 * 11646 * @method _createNewCategory 11647 * @param sCategory {String} Category name. 11648 * @private 11649 */ 11650 YAHOO.widget.Logger._createNewCategory = function(sCategory) { 11651 this.categories.push(sCategory); 11652 this.categoryCreateEvent.fire(sCategory); 11653 }; 11654 11655 /** 11656 * Checks to see if a category has already been created. 11657 * 11658 * @method _isNewCategory 11659 * @param sCategory {String} Category name. 11660 * @return {Boolean} Returns true if category is unknown, else returns false. 11661 * @private 11662 */ 11663 YAHOO.widget.Logger._isNewCategory = function(sCategory) { 11664 for(var i=0; i < this.categories.length; i++) { 11665 if(sCategory == this.categories[i]) { 11666 return false; 11667 } 11668 } 11669 return true; 11670 }; 11671 11672 /** 11673 * Creates a new source of log messages and fires sourceCreateEvent. 11674 * 11675 * @method _createNewSource 11676 * @param sSource {String} Source name. 11677 * @private 11678 */ 11679 YAHOO.widget.Logger._createNewSource = function(sSource) { 11680 this.sources.push(sSource); 11681 this.sourceCreateEvent.fire(sSource); 11682 }; 11683 11684 /** 11685 * Checks to see if a source already exists. 11686 * 11687 * @method _isNewSource 11688 * @param sSource {String} Source name. 11689 * @return {Boolean} Returns true if source is unknown, else returns false. 11690 * @private 11691 */ 11692 YAHOO.widget.Logger._isNewSource = function(sSource) { 11693 if(sSource) { 11694 for(var i=0; i < this.sources.length; i++) { 11695 if(sSource == this.sources[i]) { 11696 return false; 11697 } 11698 } 11699 return true; 11700 } 11701 }; 11702 11703 /** 11704 * Outputs a log message to global console.log() function. 11705 * 11706 * @method _printToBrowserConsole 11707 * @param oEntry {Object} Log entry object. 11708 * @private 11709 */ 11710 YAHOO.widget.Logger._printToBrowserConsole = function(oEntry) { 11711 if ((window.console && console.log) || 11712 (window.opera && opera.postError)) { 11713 var category = oEntry.category; 11714 var label = oEntry.category.substring(0,4).toUpperCase(); 11715 11716 var time = oEntry.time; 11717 var localTime; 11718 if (time.toLocaleTimeString) { 11719 localTime = time.toLocaleTimeString(); 11720 } 11721 else { 11722 localTime = time.toString(); 11723 } 11724 11725 var msecs = time.getTime(); 11726 var elapsedTime = (YAHOO.widget.Logger._lastTime) ? 11727 (msecs - YAHOO.widget.Logger._lastTime) : 0; 11728 YAHOO.widget.Logger._lastTime = msecs; 11729 11730 var output = 11731 localTime + " (" + 11732 elapsedTime + "ms): " + 11733 oEntry.source + ": "; 11734 11735 if (window.console) { 11736 console.log(output, oEntry.msg); 11737 } else { 11738 opera.postError(output + oEntry.msg); 11739 } 11740 } 11741 }; 11742 11743 ///////////////////////////////////////////////////////////////////////////// 11744 // 11745 // Private event handlers 11746 // 11747 ///////////////////////////////////////////////////////////////////////////// 11748 11749 /** 11750 * Handles logging of messages due to window error events. 11751 * 11752 * @method _onWindowError 11753 * @param sMsg {String} The error message. 11754 * @param sUrl {String} URL of the error. 11755 * @param sLine {String} Line number of the error. 11756 * @private 11757 */ 11758 YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) { 11759 // Logger is not in scope of this event handler 11760 try { 11761 YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window"); 11762 if(YAHOO.widget.Logger._origOnWindowError) { 11763 YAHOO.widget.Logger._origOnWindowError(); 11764 } 11765 } 11766 catch(e) { 11767 return false;
11768 } 11769 }; 11770 11771 ///////////////////////////////////////////////////////////////////////////// 11772 // 11773 // First log 11774 // 11775 ///////////////////////////////////////////////////////////////////////////// 11776 11777 YAHOO.widget.Logger.log("Logger initialized"); 11778} 11779 11780/****************************************************************************/ 11781/****************************************************************************/ 11782/****************************************************************************/ 11783(function () { 11784var Logger = YAHOO.widget.Logger, 11785 u = YAHOO.util, 11786 Dom = u.Dom, 11787 Event = u.Event, 11788 d = document; 11789 11790function make(el,props) { 11791 el = d.createElement(el); 11792 if (props) { 11793 for (var p in props) { 11794 if (props.hasOwnProperty(p)) { 11795 el[p] = props[p]; 11796 } 11797 } 11798 } 11799 return el; 11800} 11801 11802/** 11803 * The LogReader class provides UI to read messages logged to YAHOO.widget.Logger. 11804 * 11805 * @class LogReader 11806 * @constructor 11807 * @param elContainer {HTMLElement} (optional) DOM element reference of an existing DIV. 11808 * @param elContainer {String} (optional) String ID of an existing DIV. 11809 * @param oConfigs {Object} (optional) Object literal of configuration params. 11810 */ 11811function LogReader(elContainer, oConfigs) { 11812 this._sName = LogReader._index; 11813 LogReader._index++; 11814 11815 this._init.apply(this,arguments); 11816 11817 /** 11818 * Render the LogReader immediately upon instantiation. If set to false, 11819 * you must call myLogReader.render() to generate the UI. 11820 * 11821 * @property autoRender 11822 * @type {Boolean} 11823 * @default true 11824 */ 11825 if (this.autoRender !== false) { 11826 this.render(); 11827 } 11828} 11829 11830///////////////////////////////////////////////////////////////////////////// 11831// 11832// Static member variables 11833// 11834///////////////////////////////////////////////////////////////////////////// 11835YAHOO.lang.augmentObject(LogReader, { 11836 /** 11837 * Internal class member to index multiple LogReader instances. 11838 * 11839 * @property _memberName 11840 * @static 11841 * @type Number 11842 * @default 0 11843 * @private 11844 */ 11845 _index : 0, 11846 11847 /** 11848 * Node template for the log entries 11849 * @property ENTRY_TEMPLATE 11850 * @static 11851 * @type {HTMLElement} 11852 * @default <code>pre</code> element with class yui-log-entry 11853 */ 11854 ENTRY_TEMPLATE : (function () { 11855 return make('pre',{ className: 'yui-log-entry' }); 11856 })(), 11857 11858 /** 11859 * Template used for innerHTML of verbose entry output. 11860 * @property VERBOSE_TEMPLATE 11861 * @static 11862 * @default "<p><span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>" 11863 */ 11864 VERBOSE_TEMPLATE : "<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>", 11865 11866 /** 11867 * Template used for innerHTML of compact entry output. 11868 * @property BASIC_TEMPLATE 11869 * @static 11870 * @default "<p><span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>" 11871 */ 11872 BASIC_TEMPLATE : "<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>" 11873}); 11874 11875///////////////////////////////////////////////////////////////////////////// 11876// 11877// Public member variables 11878// 11879///////////////////////////////////////////////////////////////////////////// 11880 11881LogReader.prototype = { 11882 /** 11883 * Whether or not LogReader is enabled to output log messages. 11884 * 11885 * @property logReaderEnabled 11886 * @type Boolean 11887 * @default true 11888 */ 11889 logReaderEnabled : true, 11890 11891 /** 11892 * Public member to access CSS width of the LogReader container. 11893 * 11894 * @property width
11895 * @type String 11896 */ 11897 width : null, 11898 11899 /** 11900 * Public member to access CSS height of the LogReader container. 11901 * 11902 * @property height 11903 * @type String 11904 */ 11905 height : null, 11906 11907 /** 11908 * Public member to access CSS top position of the LogReader container. 11909 * 11910 * @property top 11911 * @type String 11912 */ 11913 top : null, 11914 11915 /** 11916 * Public member to access CSS left position of the LogReader container. 11917 * 11918 * @property left 11919 * @type String 11920 */ 11921 left : null, 11922 11923 /** 11924 * Public member to access CSS right position of the LogReader container. 11925 * 11926 * @property right 11927 * @type String 11928 */ 11929 right : null, 11930 11931 /** 11932 * Public member to access CSS bottom position of the LogReader container. 11933 * 11934 * @property bottom 11935 * @type String 11936 */ 11937 bottom : null, 11938 11939 /** 11940 * Public member to access CSS font size of the LogReader container. 11941 * 11942 * @property fontSize 11943 * @type String 11944 */ 11945 fontSize : null, 11946 11947 /** 11948 * Whether or not the footer UI is enabled for the LogReader. 11949 * 11950 * @property footerEnabled 11951 * @type Boolean 11952 * @default true 11953 */ 11954 footerEnabled : true, 11955 11956 /** 11957 * Whether or not output is verbose (more readable). Setting to true will make 11958 * output more compact (less readable). 11959 * 11960 * @property verboseOutput 11961 * @type Boolean 11962 * @default true 11963 */ 11964 verboseOutput : true, 11965 11966 /** 11967 * Custom output format for log messages. Defaults to null, which falls 11968 * back to verboseOutput param deciding between LogReader.VERBOSE_TEMPLATE 11969 * and LogReader.BASIC_TEMPLATE. Use bracketed place holders to mark where 11970 * message info should go. Available place holder names include: 11971 * <ul> 11972 * <li>category</li> 11973 * <li>label</li> 11974 * <li>sourceAndDetail</li> 11975 * <li>message</li> 11976 * <li>localTime</li> 11977 * <li>elapsedTime</li> 11978 * <li>totalTime</li> 11979 * </ul> 11980 * 11981 * @property entryFormat
11982 * @type String 11983 * @default null 11984 */ 11985 entryFormat : null, 11986 11987 /** 11988 * Whether or not newest message is printed on top. 11989 * 11990 * @property newestOnTop 11991 * @type Boolean 11992 */ 11993 newestOnTop : true, 11994 11995 /** 11996 * Output timeout buffer in milliseconds. 11997 * 11998 * @property outputBuffer 11999 * @type Number 12000 * @default 100 12001 */ 12002 outputBuffer : 100, 12003 12004 /** 12005 * Maximum number of messages a LogReader console will display. 12006 * 12007 * @property thresholdMax 12008 * @type Number 12009 * @default 500 12010 */ 12011 thresholdMax : 500, 12012 12013 /** 12014 * When a LogReader console reaches its thresholdMax, it will clear out messages 12015 * and print out the latest thresholdMin number of messages. 12016 * 12017 * @property thresholdMin 12018 * @type Number 12019 * @default 100 12020 */ 12021 thresholdMin : 100, 12022 12023 /** 12024 * True when LogReader is in a collapsed state, false otherwise. 12025 * 12026 * @property isCollapsed 12027 * @type Boolean 12028 * @default false 12029 */ 12030 isCollapsed : false, 12031 12032 /** 12033 * True when LogReader is in a paused state, false otherwise. 12034 * 12035 * @property isPaused 12036 * @type Boolean 12037 * @default false 12038 */ 12039 isPaused : false, 12040 12041 /** 12042 * Enables draggable LogReader if DragDrop Utility is present. 12043 * 12044 * @property draggable 12045 * @type Boolean 12046 * @default true 12047 */ 12048 draggable : true, 12049 12050 ///////////////////////////////////////////////////////////////////////////// 12051 // 12052 // Public methods 12053 // 12054 ///////////////////////////////////////////////////////////////////////////// 12055 12056 /** 12057 * Public accessor to the unique name of the LogReader instance. 12058 * 12059 * @method toString 12060 * @return {String} Unique name of the LogReader instance. 12061 */ 12062 toString : function() { 12063 return "LogReader instance" + this._sName; 12064 }, 12065 /** 12066 * Pauses output of log messages. While paused, log messages are not lost, but 12067 * get saved to a buffer and then output upon resume of LogReader. 12068 * 12069 * @method pause 12070 */ 12071 pause : function() { 12072 this.isPaused = true; 12073 this._timeout = null; 12074 this.logReaderEnabled = false; 12075 if (this._btnPause) { 12076 this._btnPause.value = "Resume"; 12077 } 12078 }, 12079 12080 /** 12081 * Resumes output of log messages, including outputting any log messages that 12082 * have been saved to buffer while paused. 12083 * 12084 * @method resume 12085 */ 12086 resume : function() { 12087 this.isPaused = false; 12088 this.logReaderEnabled = true; 12089 this._printBuffer(); 12090 if (this._btnPause) { 12091 this._btnPause.value = "Pause"; 12092 } 12093 }, 12094 12095 /** 12096 * Adds the UI to the DOM, attaches event listeners, and bootstraps initial 12097 * UI state. 12098 * 12099 * @method render 12100 */ 12101 render : function () { 12102 if (this.rendered) { 12103 return; 12104 } 12105 12106 this._initContainerEl(); 12107 12108 this._initHeaderEl(); 12109 this._initConsoleEl(); 12110 this._initFooterEl(); 12111 12112 this._initCategories(); 12113 this._initSources(); 12114 12115 this._initDragDrop(); 12116 12117 // Subscribe to Logger custom events 12118 Logger.newLogEvent.subscribe(this._onNewLog, this); 12119 Logger.logResetEvent.subscribe(this._onReset, this); 12120 12121 Logger.categoryCreateEvent.subscribe(this._onCategoryCreate, this); 12122 Logger.sourceCreateEvent.subscribe(this._onSourceCreate, this); 12123 12124 this.rendered = true; 12125 12126 this._filterLogs(); 12127 }, 12128 12129 /** 12130 * Removes the UI from the DOM entirely and detaches all event listeners. 12131 * Implementers should note that Logger will still accumulate messages. 12132 * 12133 * @method destroy 12134 */ 12135 destroy : function () { 12136 Event.purgeElement(this._elContainer,true); 12137 this._elContainer.innerHTML = ''; 12138 this._elContainer.parentNode.removeChild(this._elContainer); 12139 12140 this.rendered = false; 12141 }, 12142 12143 /** 12144 * Hides UI of LogReader. Logging functionality is not disrupted. 12145 * 12146 * @method hide 12147 */ 12148 hide : function() { 12149 this._elContainer.style.display = "none"; 12150 }, 12151 12152 /** 12153 * Shows UI of LogReader. Logging functionality is not disrupted. 12154 * 12155 * @method show 12156 */ 12157 show : function() { 12158 this._elContainer.style.display = "block"; 12159 }, 12160 12161 /** 12162 * Collapses UI of LogReader. Logging functionality is not disrupted. 12163 * 12164 * @method collapse 12165 */ 12166 collapse : function() { 12167 this._elConsole.style.display = "none"; 12168 if(this._elFt) { 12169 this._elFt.style.display = "none"; 12170 } 12171 this._btnCollapse.value = "Expand"; 12172 this.isCollapsed = true; 12173 }, 12174 12175 /** 12176 * Expands UI of LogReader. Logging functionality is not disrupted. 12177 * 12178 * @method expand 12179 */ 12180 expand : function() { 12181 this._elConsole.style.display = "block"; 12182 if(this._elFt) { 12183 this._elFt.style.display = "block"; 12184 } 12185 this._btnCollapse.value = "Collapse"; 12186 this.isCollapsed = false;
12187 }, 12188 12189 /** 12190 * Returns related checkbox element for given filter (i.e., category or source). 12191 * 12192 * @method getCheckbox 12193 * @param {String} Category or source name. 12194 * @return {Array} Array of all filter checkboxes. 12195 */ 12196 getCheckbox : function(filter) { 12197 return this._filterCheckboxes[filter]; 12198 }, 12199 12200 /** 12201 * Returns array of enabled categories. 12202 * 12203 * @method getCategories 12204 * @return {String[]} Array of enabled categories. 12205 */ 12206 getCategories : function() { 12207 return this._categoryFilters; 12208 }, 12209 12210 /** 12211 * Shows log messages associated with given category. 12212 * 12213 * @method showCategory 12214 * @param {String} Category name. 12215 */ 12216 showCategory : function(sCategory) { 12217 var filtersArray = this._categoryFilters; 12218 // Don't do anything if category is already enabled 12219 // Use Array.indexOf if available... 12220 if(filtersArray.indexOf) { 12221 if(filtersArray.indexOf(sCategory) > -1) { 12222 return; 12223 } 12224 } 12225 // ...or do it the old-fashioned way 12226 else { 12227 for(var i=0; i<filtersArray.length; i++) { 12228 if(filtersArray[i] === sCategory){ 12229 return; 12230 } 12231 } 12232 } 12233 12234 this._categoryFilters.push(sCategory); 12235 this._filterLogs(); 12236 var elCheckbox = this.getCheckbox(sCategory); 12237 if(elCheckbox) { 12238 elCheckbox.checked = true; 12239 } 12240 }, 12241 12242 /** 12243 * Hides log messages associated with given category. 12244 * 12245 * @method hideCategory 12246 * @param {String} Category name. 12247 */ 12248 hideCategory : function(sCategory) { 12249 var filtersArray = this._categoryFilters; 12250 for(var i=0; i<filtersArray.length; i++) { 12251 if(sCategory == filtersArray[i]) { 12252 filtersArray.splice(i, 1); 12253 break; 12254 } 12255 } 12256 this._filterLogs(); 12257 var elCheckbox = this.getCheckbox(sCategory); 12258 if(elCheckbox) { 12259 elCheckbox.checked = false; 12260 } 12261 }, 12262 12263 /** 12264 * Returns array of enabled sources. 12265 * 12266 * @method getSources 12267 * @return {Array} Array of enabled sources. 12268 */ 12269 getSources : function() { 12270 return this._sourceFilters; 12271 }, 12272 12273 /** 12274 * Shows log messages associated with given source. 12275 * 12276 * @method showSource 12277 * @param {String} Source name. 12278 */ 12279 showSource : function(sSource) { 12280 var filtersArray = this._sourceFilters; 12281 // Don't do anything if category is already enabled 12282 // Use Array.indexOf if available... 12283 if(filtersArray.indexOf) { 12284 if(filtersArray.indexOf(sSource) > -1) { 12285 return; 12286 } 12287 } 12288 // ...or do it the old-fashioned way 12289 else { 12290 for(var i=0; i<filtersArray.length; i++) { 12291 if(sSource == filtersArray[i]){ 12292 return; 12293 } 12294 } 12295 } 12296 filtersArray.push(sSource); 12297 this._filterLogs(); 12298 var elCheckbox = this.getCheckbox(sSource); 12299 if(elCheckbox) { 12300 elCheckbox.checked = true; 12301 } 12302 }, 12303 12304 /** 12305 * Hides log messages associated with given source. 12306 * 12307 * @method hideSource 12308 * @param {String} Source name. 12309 */ 12310 hideSource : function(sSource) { 12311 var filtersArray = this._sourceFilters; 12312 for(var i=0; i<filtersArray.length; i++) { 12313 if(sSource == filtersArray[i]) { 12314 filtersArray.splice(i, 1); 12315 break; 12316 } 12317 } 12318 this._filterLogs(); 12319 var elCheckbox = this.getCheckbox(sSource); 12320 if(elCheckbox) { 12321 elCheckbox.checked = false; 12322 } 12323 }, 12324 12325 /** 12326 * Does not delete any log messages, but clears all printed log messages from 12327 * the console. Log messages will be printed out again if user re-filters. The 12328 * static method YAHOO.widget.Logger.reset() should be called in order to 12329 * actually delete log messages. 12330 * 12331 * @method clearConsole 12332 */ 12333 clearConsole : function() {
12334 // Clear the buffer of any pending messages 12335 this._timeout = null; 12336 this._buffer = []; 12337 this._consoleMsgCount = 0; 12338 12339 var elConsole = this._elConsole; 12340 elConsole.innerHTML = ''; 12341 }, 12342 12343 /** 12344 * Updates title to given string. 12345 * 12346 * @method setTitle 12347 * @param sTitle {String} New title. 12348 */ 12349 setTitle : function(sTitle) { 12350 this._title.innerHTML = this.html2Text(sTitle); 12351 }, 12352 12353 /** 12354 * Gets timestamp of the last log. 12355 * 12356 * @method getLastTime 12357 * @return {Date} Timestamp of the last log. 12358 */ 12359 getLastTime : function() { 12360 return this._lastTime; 12361 }, 12362 12363 formatMsg : function (entry) { 12364 var entryFormat = this.entryFormat || (this.verboseOutput ? 12365 LogReader.VERBOSE_TEMPLATE : LogReader.BASIC_TEMPLATE), 12366 info = { 12367 category : entry.category, 12368 12369 // Label for color-coded display 12370 label : entry.category.substring(0,4).toUpperCase(), 12371 12372 sourceAndDetail : entry.sourceDetail ? 12373 entry.source + " " + entry.sourceDetail : 12374 entry.source, 12375 12376 // Escape HTML entities in the log message itself for output 12377 // to console 12378 message : this.html2Text(entry.msg || entry.message || '') 12379 }; 12380 12381 // Add time info 12382 if (entry.time && entry.time.getTime) { 12383 info.localTime = entry.time.toLocaleTimeString ? 12384 entry.time.toLocaleTimeString() : 12385 entry.time.toString(); 12386 12387 // Calculate the elapsed time to be from the last item that 12388 // passed through the filter, not the absolute previous item 12389 // in the stack 12390 info.elapsedTime = entry.time.getTime() - this.getLastTime(); 12391 12392 info.totalTime = entry.time.getTime() - Logger.getStartTime(); 12393 } 12394 12395 var msg = LogReader.ENTRY_TEMPLATE.cloneNode(true); 12396 if (this.verboseOutput) { 12397 msg.className += ' yui-log-verbose'; 12398 } 12399 12400 // Bug 2061169: Workaround for YAHOO.lang.substitute() 12401 msg.innerHTML = entryFormat.replace(/\{(\w+)\}/g, 12402 function (x, placeholder) { 12403 return (placeholder in info) ? info[placeholder] : ''; 12404 }); 12405 12406 return msg; 12407 }, 12408 12409 /** 12410 * Converts input chars "<", ">", and "&" to HTML entities. 12411 * 12412 * @method html2Text 12413 * @param sHtml {String} String to convert. 12414 * @private 12415 */ 12416 html2Text : function(sHtml) { 12417 if(sHtml) { 12418 sHtml += ""; 12419 return sHtml.replace(/&/g, "&"). 12420 replace(/</g, "<"). 12421 replace(/>/g, ">"); 12422 } 12423 return ""; 12424 }, 12425 12426///////////////////////////////////////////////////////////////////////////// 12427// 12428// Private member variables 12429// 12430///////////////////////////////////////////////////////////////////////////// 12431 12432 /** 12433 * Name of LogReader instance. 12434 * 12435 * @property _sName
12436 * @type String 12437 * @private 12438 */ 12439 _sName : null, 12440 12441 //TODO: remove 12442 /** 12443 * A class member shared by all LogReaders if a container needs to be 12444 * created during instantiation. Will be null if a container element never needs to 12445 * be created on the fly, such as when the implementer passes in their own element. 12446 * 12447 * @property _elDefaultContainer 12448 * @type HTMLElement 12449 * @private 12450 */ 12451 //YAHOO.widget.LogReader._elDefaultContainer = null; 12452 12453 /** 12454 * Buffer of log message objects for batch output. 12455 * 12456 * @property _buffer 12457 * @type Object[] 12458 * @private 12459 */ 12460 _buffer : null, 12461 12462 /** 12463 * Number of log messages output to console. 12464 * 12465 * @property _consoleMsgCount 12466 * @type Number 12467 * @default 0 12468 * @private 12469 */ 12470 _consoleMsgCount : 0, 12471 12472 /** 12473 * Date of last output log message. 12474 * 12475 * @property _lastTime 12476 * @type Date 12477 * @private 12478 */ 12479 _lastTime : null, 12480 12481 /** 12482 * Batched output timeout ID. 12483 * 12484 * @property _timeout 12485 * @type Number 12486 * @private 12487 */ 12488 _timeout : null, 12489 12490 /** 12491 * Hash of filters and their related checkbox elements. 12492 * 12493 * @property _filterCheckboxes 12494 * @type Object 12495 * @private 12496 */ 12497 _filterCheckboxes : null, 12498 12499 /** 12500 * Array of filters for log message categories. 12501 * 12502 * @property _categoryFilters
12503 * @type String[] 12504 * @private 12505 */ 12506 _categoryFilters : null, 12507 12508 /** 12509 * Array of filters for log message sources. 12510 * 12511 * @property _sourceFilters 12512 * @type String[] 12513 * @private 12514 */ 12515 _sourceFilters : null, 12516 12517 /** 12518 * LogReader container element. 12519 * 12520 * @property _elContainer 12521 * @type HTMLElement 12522 * @private 12523 */ 12524 _elContainer : null, 12525 12526 /** 12527 * LogReader header element. 12528 * 12529 * @property _elHd 12530 * @type HTMLElement 12531 * @private 12532 */ 12533 _elHd : null, 12534 12535 /** 12536 * LogReader collapse element. 12537 * 12538 * @property _elCollapse 12539 * @type HTMLElement 12540 * @private 12541 */ 12542 _elCollapse : null, 12543 12544 /** 12545 * LogReader collapse button element. 12546 * 12547 * @property _btnCollapse 12548 * @type HTMLElement 12549 * @private 12550 */ 12551 _btnCollapse : null, 12552 12553 /** 12554 * LogReader title header element. 12555 * 12556 * @property _title 12557 * @type HTMLElement 12558 * @private 12559 */ 12560 _title : null, 12561 12562 /** 12563 * LogReader console element. 12564 * 12565 * @property _elConsole 12566 * @type HTMLElement 12567 * @private 12568 */ 12569 _elConsole : null, 12570 12571 /** 12572 * LogReader footer element. 12573 * 12574 * @property _elFt 12575 * @type HTMLElement 12576 * @private 12577 */ 12578 _elFt : null, 12579 12580 /** 12581 * LogReader buttons container element. 12582 * 12583 * @property _elBtns 12584 * @type HTMLElement 12585 * @private 12586 */ 12587 _elBtns : null, 12588 12589 /** 12590 * Container element for LogReader category filter checkboxes. 12591 * 12592 * @property _elCategoryFilters 12593 * @type HTMLElement 12594 * @private 12595 */ 12596 _elCategoryFilters : null, 12597 12598 /** 12599 * Container element for LogReader source filter checkboxes. 12600 * 12601 * @property _elSourceFilters 12602 * @type HTMLElement 12603 * @private 12604 */ 12605 _elSourceFilters : null, 12606 12607 /** 12608 * LogReader pause button element. 12609 * 12610 * @property _btnPause 12611 * @type HTMLElement 12612 * @private 12613 */ 12614 _btnPause : null, 12615 12616 /** 12617 * Clear button element. 12618 * 12619 * @property _btnClear 12620 * @type HTMLElement 12621 * @private 12622 */ 12623 _btnClear : null, 12624 12625 ///////////////////////////////////////////////////////////////////////////// 12626 // 12627 // Private methods 12628 // 12629 ///////////////////////////////////////////////////////////////////////////// 12630 12631 /** 12632 * Initializes the instance's message buffer, start time, etc 12633 * 12634 * @method _init 12635 * @param container {String|HTMLElement} (optional) the render target 12636 * @param config {Object} (optional) instance configuration 12637 * @protected 12638 */ 12639 _init : function (container, config) { 12640 // Internal vars 12641 this._buffer = []; // output buffer 12642 this._filterCheckboxes = {}; // pointers to checkboxes 12643 this._lastTime = Logger.getStartTime(); // timestamp of last log message to console 12644 12645 // Parse config vars here 12646 if (config && (config.constructor == Object)) { 12647 for(var param in config) { 12648 if (config.hasOwnProperty(param)) { 12649 this[param] = config[param]; 12650 } 12651 } 12652 } 12653 12654 this._elContainer = Dom.get(container); 12655 12656 YAHOO.log("LogReader initialized", null, this.toString()); 12657 }, 12658 12659 /** 12660 * Initializes the primary container element. 12661 * 12662 * @method _initContainerEl 12663 * @private 12664 */ 12665 _initContainerEl : function() { 12666 12667 // Default the container if unset or not a div 12668 if(!this._elContainer || !/div$/i.test(this._elContainer.tagName)) { 12669 this._elContainer = d.body.insertBefore(make("div"),d.body.firstChild); 12670 // Only position absolutely if an in-DOM element is not supplied 12671 Dom.addClass(this._elContainer,"yui-log-container"); 12672 } 12673 12674 Dom.addClass(this._elContainer,"yui-log"); 12675 12676 // If implementer has provided container values, trust and set those 12677 var style = this._elContainer.style, 12678 styleProps = ['width','right','top','fontSize'], 12679 prop,i; 12680 12681 for (i = styleProps.length - 1; i >= 0; --i) { 12682 prop = styleProps[i]; 12683 if (this[prop]){ 12684 style[prop] = this[prop]; 12685 } 12686 } 12687 12688 if(this.left) { 12689 style.left = this.left; 12690 style.right = "auto"; 12691 } 12692 if(this.bottom) { 12693 style.bottom = this.bottom; 12694 style.top = "auto"; 12695 } 12696 12697 // Opera needs a little prodding to reflow sometimes 12698 if (YAHOO.env.ua.opera) { 12699 d.body.style += ''; 12700 } 12701 12702 }, 12703 12704 /** 12705 * Initializes the header element. 12706 * 12707 * @method _initHeaderEl 12708 * @private 12709 */ 12710 _initHeaderEl : function() { 12711 // Destroy header if present 12712 if(this._elHd) { 12713 // Unhook DOM events 12714 Event.purgeElement(this._elHd, true); 12715 12716 // Remove DOM elements 12717 this._elHd.innerHTML = ""; 12718 } 12719 12720 // Create header 12721 // TODO: refactor this into an innerHTML 12722 this._elHd = make("div",{ 12723 className: "yui-log-hd" 12724 }); 12725 Dom.generateId(this._elHd, 'yui-log-hd' + this._sName); 12726 12727 this._elCollapse = make("div",{ className: 'yui-log-btns' }); 12728 12729 this._btnCollapse = make("input",{ 12730 type: 'button', 12731 className: 'yui-log-button', 12732 value: 'Collapse' 12733 }); 12734 Event.on(this._btnCollapse,'click',this._onClickCollapseBtn,this); 12735 12736
12737 this._title = make("h4",{ innerHTML : "Logger Console" }); 12738 12739 this._elCollapse.appendChild(this._btnCollapse); 12740 this._elHd.appendChild(this._elCollapse); 12741 this._elHd.appendChild(this._title); 12742 this._elContainer.appendChild(this._elHd); 12743 }, 12744 12745 /** 12746 * Initializes the console element. 12747 * 12748 * @method _initConsoleEl 12749 * @private 12750 */ 12751 _initConsoleEl : function() { 12752 // Destroy console 12753 if(this._elConsole) { 12754 // Unhook DOM events 12755 Event.purgeElement(this._elConsole, true); 12756 12757 // Remove DOM elements 12758 this._elConsole.innerHTML = ""; 12759 } 12760 12761 // Ceate console 12762 this._elConsole = make("div", { className: "yui-log-bd" }); 12763 12764 // If implementer has provided console, trust and set those 12765 if(this.height) { 12766 this._elConsole.style.height = this.height; 12767 } 12768 12769 this._elContainer.appendChild(this._elConsole); 12770 }, 12771 12772 /** 12773 * Initializes the footer element. 12774 * 12775 * @method _initFooterEl 12776 * @private 12777 */ 12778 _initFooterEl : function() { 12779 // Don't create footer elements if footer is disabled 12780 if(this.footerEnabled) { 12781 // Destroy console 12782 if(this._elFt) { 12783 // Unhook DOM events 12784 Event.purgeElement(this._elFt, true); 12785 12786 // Remove DOM elements 12787 this._elFt.innerHTML = ""; 12788 } 12789 12790 // TODO: use innerHTML 12791 this._elFt = make("div",{ className: "yui-log-ft" }); 12792 this._elBtns = make("div", { className: "yui-log-btns" }); 12793 this._btnPause = make("input", { 12794 type: "button", 12795 className: "yui-log-button", 12796 value: "Pause" 12797 }); 12798 12799 Event.on(this._btnPause,'click',this._onClickPauseBtn,this); 12800 12801 this._btnClear = make("input", { 12802 type: "button", 12803 className: "yui-log-button", 12804 value: "Clear" 12805 }); 12806 12807 Event.on(this._btnClear,'click',this._onClickClearBtn,this); 12808 12809 this._elCategoryFilters = make("div", { className: "yui-log-categoryfilters" }); 12810 this._elSourceFilters = make("div", { className: "yui-log-sourcefilters" }); 12811 12812 this._elBtns.appendChild(this._btnPause); 12813 this._elBtns.appendChild(this._btnClear); 12814 this._elFt.appendChild(this._elBtns); 12815 this._elFt.appendChild(this._elCategoryFilters); 12816 this._elFt.appendChild(this._elSourceFilters); 12817 this._elContainer.appendChild(this._elFt); 12818 } 12819 }, 12820 12821 /** 12822 * Initializes Drag and Drop on the header element. 12823 * 12824 * @method _initDragDrop 12825 * @private 12826 */ 12827 _initDragDrop : function() { 12828 // If Drag and Drop utility is available... 12829 // ...and draggable is true... 12830 // ...then make the header draggable 12831 if(u.DD && this.draggable && this._elHd) { 12832 var ylog_dd = new u.DD(this._elContainer); 12833 ylog_dd.setHandleElId(this._elHd.id); 12834 //TODO: use class name 12835 this._elHd.style.cursor = "move"; 12836 } 12837 }, 12838 12839 /** 12840 * Initializes category filters. 12841 * 12842 * @method _initCategories 12843 * @private 12844 */ 12845 _initCategories : function() { 12846 // Initialize category filters 12847 this._categoryFilters = []; 12848 var aInitialCategories = Logger.categories; 12849 12850 for(var j=0; j < aInitialCategories.length; j++) { 12851 var sCategory = aInitialCategories[j]; 12852 12853 // Add category to the internal array of filters 12854 this._categoryFilters.push(sCategory); 12855 12856 // Add checkbox element if UI is enabled 12857 if(this._elCategoryFilters) { 12858 this._createCategoryCheckbox(sCategory); 12859 } 12860 } 12861 }, 12862 12863 /** 12864 * Initializes source filters. 12865 * 12866 * @method _initSources 12867 * @private 12868 */ 12869 _initSources : function() { 12870 // Initialize source filters 12871 this._sourceFilters = []; 12872 var aInitialSources = Logger.sources; 12873 12874 for(var j=0; j < aInitialSources.length; j++) { 12875 var sSource = aInitialSources[j]; 12876 12877 // Add source to the internal array of filters 12878 this._sourceFilters.push(sSource); 12879 12880 // Add checkbox element if UI is enabled 12881 if(this._elSourceFilters) { 12882 this._createSourceCheckbox(sSource); 12883 } 12884 } 12885 }, 12886 12887 /** 12888 * Creates the UI for a category filter in the LogReader footer element. 12889 *
12890 * @method _createCategoryCheckbox 12891 * @param sCategory {String} Category name. 12892 * @private 12893 */ 12894 _createCategoryCheckbox : function(sCategory) { 12895 if(this._elFt) { 12896 var filter = make("span",{ className: "yui-log-filtergrp" }), 12897 checkid = Dom.generateId(null, "yui-log-filter-" + sCategory + this._sName), 12898 check = make("input", { 12899 id: checkid, 12900 className: "yui-log-filter-" + sCategory, 12901 type: "checkbox", 12902 category: sCategory 12903 }), 12904 label = make("label", { 12905 htmlFor: checkid, 12906 className: sCategory, 12907 innerHTML: sCategory 12908 }); 12909 12910 12911 // Subscribe to the click event 12912 Event.on(check,'click',this._onCheckCategory,this); 12913 12914 this._filterCheckboxes[sCategory] = check; 12915 12916 // Append el at the end so IE 5.5 can set "type" attribute 12917 // and THEN set checked property 12918 filter.appendChild(check); 12919 filter.appendChild(label); 12920 this._elCategoryFilters.appendChild(filter); 12921 check.checked = true; 12922 } 12923 }, 12924 12925 /** 12926 * Creates a checkbox in the LogReader footer element to filter by source. 12927 * 12928 * @method _createSourceCheckbox 12929 * @param sSource {String} Source name. 12930 * @private 12931 */ 12932 _createSourceCheckbox : function(sSource) { 12933 if(this._elFt) { 12934 var filter = make("span",{ className: "yui-log-filtergrp" }), 12935 checkid = Dom.generateId(null, "yui-log-filter-" + sSource + this._sName), 12936 check = make("input", { 12937 id: checkid, 12938 className: "yui-log-filter-" + sSource, 12939 type: "checkbox", 12940 source: sSource 12941 }), 12942 label = make("label", { 12943 htmlFor: checkid, 12944 className: sSource, 12945 innerHTML: sSource 12946 }); 12947 12948 12949 // Subscribe to the click event 12950 Event.on(check,'click',this._onCheckSource,this); 12951 12952 this._filterCheckboxes[sSource] = check; 12953 12954 // Append el at the end so IE 5.5 can set "type" attribute 12955 // and THEN set checked property 12956 filter.appendChild(check); 12957 filter.appendChild(label); 12958 this._elSourceFilters.appendChild(filter); 12959 check.checked = true; 12960 } 12961 }, 12962 12963 /** 12964 * Reprints all log messages in the stack through filters. 12965 * 12966 * @method _filterLogs 12967 * @private 12968 */ 12969 _filterLogs : function() { 12970 // Reprint stack with new filters 12971 if (this._elConsole !== null) { 12972 this.clearConsole(); 12973 this._printToConsole(Logger.getStack()); 12974 } 12975 }, 12976 12977 /** 12978 * Sends buffer of log messages to output and clears buffer. 12979 * 12980 * @method _printBuffer 12981 * @private 12982 */ 12983 _printBuffer : function() { 12984 this._timeout = null; 12985 12986 if(this._elConsole !== null) { 12987 var thresholdMax = this.thresholdMax; 12988 thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500; 12989 if(this._consoleMsgCount < thresholdMax) { 12990 var entries = []; 12991 for (var i=0; i<this._buffer.length; i++) { 12992 entries[i] = this._buffer[i]; 12993 } 12994 this._buffer = []; 12995 this._printToConsole(entries); 12996 } 12997 else { 12998 this._filterLogs(); 12999 } 13000 13001 if(!this.newestOnTop) { 13002 this._elConsole.scrollTop = this._elConsole.scrollHeight; 13003 } 13004 } 13005 }, 13006 13007 /** 13008 * Cycles through an array of log messages, and outputs each one to the console 13009 * if its category has not been filtered out. 13010 * 13011 * @method _printToConsole 13012 * @param aEntries {Object[]} Array of LogMsg objects to output to console. 13013 * @private 13014 */ 13015 _printToConsole : function(aEntries) { 13016 // Manage the number of messages displayed in the console 13017 var entriesLen = aEntries.length, 13018 df = d.createDocumentFragment(), 13019 msgHTML = [], 13020 thresholdMin = this.thresholdMin, 13021 sourceFiltersLen = this._sourceFilters.length, 13022 categoryFiltersLen = this._categoryFilters.length,
13023 entriesStartIndex, 13024 i, j, msg, before; 13025 13026 if(isNaN(thresholdMin) || (thresholdMin > this.thresholdMax)) { 13027 thresholdMin = 0; 13028 } 13029 entriesStartIndex = (entriesLen > thresholdMin) ? (entriesLen - thresholdMin) : 0; 13030 13031 // Iterate through all log entries 13032 for(i=entriesStartIndex; i<entriesLen; i++) { 13033 // Print only the ones that filter through 13034 var okToPrint = false, 13035 okToFilterCats = false, 13036 entry = aEntries[i], 13037 source = entry.source, 13038 category = entry.category; 13039 13040 for(j=0; j<sourceFiltersLen; j++) { 13041 if(source == this._sourceFilters[j]) { 13042 okToFilterCats = true; 13043 break; 13044 } 13045 } 13046 if(okToFilterCats) { 13047 for(j=0; j<categoryFiltersLen; j++) { 13048 if(category == this._categoryFilters[j]) { 13049 okToPrint = true; 13050 break; 13051 } 13052 } 13053 } 13054 if(okToPrint) { 13055 // Start from 0ms elapsed time 13056 if (this._consoleMsgCount === 0) { 13057 this._lastTime = entry.time.getTime(); 13058 } 13059 13060 msg = this.formatMsg(entry); 13061 if (typeof msg === 'string') { 13062 msgHTML[msgHTML.length] = msg; 13063 } else { 13064 df.insertBefore(msg, this.newestOnTop ? 13065 df.firstChild || null : null); 13066 } 13067 this._consoleMsgCount++; 13068 this._lastTime = entry.time.getTime(); 13069 } 13070 } 13071 13072 if (msgHTML.length) { 13073 msgHTML.splice(0,0,this._elConsole.innerHTML); 13074 this._elConsole.innerHTML = this.newestOnTop ? 13075 msgHTML.reverse().join('') : 13076 msgHTML.join(''); 13077 } else if (df.firstChild) { 13078 this._elConsole.insertBefore(df, this.newestOnTop ? 13079 this._elConsole.firstChild || null : null); 13080 } 13081 }, 13082 13083///////////////////////////////////////////////////////////////////////////// 13084// 13085// Private event handlers 13086// 13087///////////////////////////////////////////////////////////////////////////// 13088 13089 /** 13090 * Handles Logger's categoryCreateEvent. 13091 * 13092 * @method _onCategoryCreate 13093 * @param sType {String} The event. 13094 * @param aArgs {Object[]} Data passed from event firer. 13095 * @param oSelf {Object} The LogReader instance. 13096 * @private 13097 */ 13098 _onCategoryCreate : function(sType, aArgs, oSelf) { 13099 var category = aArgs[0]; 13100 13101 // Add category to the internal array of filters 13102 oSelf._categoryFilters.push(category); 13103 13104 if(oSelf._elFt) { 13105 oSelf._createCategoryCheckbox(category); 13106 } 13107 }, 13108 13109 /** 13110 * Handles Logger's sourceCreateEvent. 13111 * 13112 * @method _onSourceCreate 13113 * @param sType {String} The event. 13114 * @param aArgs {Object[]} Data passed from event firer. 13115 * @param oSelf {Object} The LogReader instance. 13116 * @private 13117 */ 13118 _onSourceCreate : function(sType, aArgs, oSelf) { 13119 var source = aArgs[0]; 13120 13121 // Add source to the internal array of filters 13122 oSelf._sourceFilters.push(source); 13123 13124 if(oSelf._elFt) { 13125 oSelf._createSourceCheckbox(source); 13126 } 13127 }, 13128 13129 /** 13130 * Handles check events on the category filter checkboxes. 13131 * 13132 * @method _onCheckCategory 13133 * @param v {HTMLEvent} The click event. 13134 * @param oSelf {Object} The LogReader instance. 13135 * @private 13136 */ 13137 _onCheckCategory : function(v, oSelf) { 13138 var category = this.category; 13139 if(!this.checked) { 13140 oSelf.hideCategory(category); 13141 } 13142 else { 13143 oSelf.showCategory(category); 13144 } 13145 }, 13146 13147 /** 13148 * Handles check events on the category filter checkboxes. 13149 * 13150 * @method _onCheckSource 13151 * @param v {HTMLEvent} The click event. 13152 * @param oSelf {Object} The LogReader instance. 13153 * @private 13154 */ 13155 _onCheckSource : function(v, oSelf) { 13156 var source = this.source; 13157 if(!this.checked) { 13158 oSelf.hideSource(source); 13159 } 13160 else { 13161 oSelf.showSource(source); 13162 } 13163 }, 13164 13165 /** 13166 * Handles click events on the collapse button. 13167 * 13168 * @method _onClickCollapseBtn 13169 * @param v {HTMLEvent} The click event. 13170 * @param oSelf {Object} The LogReader instance 13171 * @private 13172 */ 13173 _onClickCollapseBtn : function(v, oSelf) { 13174 if(!oSelf.isCollapsed) { 13175 oSelf.collapse(); 13176 } 13177 else { 13178 oSelf.expand(); 13179 } 13180 }, 13181 13182 /** 13183 * Handles click events on the pause button. 13184 *
13185 * @method _onClickPauseBtn 13186 * @param v {HTMLEvent} The click event. 13187 * @param oSelf {Object} The LogReader instance. 13188 * @private 13189 */ 13190 _onClickPauseBtn : function(v, oSelf) { 13191 if(!oSelf.isPaused) { 13192 oSelf.pause(); 13193 } 13194 else { 13195 oSelf.resume(); 13196 } 13197 }, 13198 13199 /** 13200 * Handles click events on the clear button. 13201 * 13202 * @method _onClickClearBtn 13203 * @param v {HTMLEvent} The click event. 13204 * @param oSelf {Object} The LogReader instance. 13205 * @private 13206 */ 13207 _onClickClearBtn : function(v, oSelf) { 13208 oSelf.clearConsole(); 13209 }, 13210 13211 /** 13212 * Handles Logger's newLogEvent. 13213 * 13214 * @method _onNewLog 13215 * @param sType {String} The event. 13216 * @param aArgs {Object[]} Data passed from event firer. 13217 * @param oSelf {Object} The LogReader instance. 13218 * @private 13219 */ 13220 _onNewLog : function(sType, aArgs, oSelf) { 13221 var logEntry = aArgs[0]; 13222 oSelf._buffer.push(logEntry); 13223 13224 if (oSelf.logReaderEnabled === true && oSelf._timeout === null) { 13225 oSelf._timeout = setTimeout(function(){oSelf._printBuffer();}, oSelf.outputBuffer); 13226 } 13227 }, 13228 13229 /** 13230 * Handles Logger's resetEvent. 13231 * 13232 * @method _onReset 13233 * @param sType {String} The event. 13234 * @param aArgs {Object[]} Data passed from event firer. 13235 * @param oSelf {Object} The LogReader instance. 13236 * @private 13237 */ 13238 _onReset : function(sType, aArgs, oSelf) { 13239 oSelf._filterLogs(); 13240 } 13241}; 13242 13243YAHOO.widget.LogReader = LogReader; 13244 13245})(); 13246YAHOO.register("logger", YAHOO.widget.Logger, {version: "2.9.0", build: "2800"}); 13247/* 13248Copyright (c) 2011, Yahoo! Inc. All rights reserved. 13249Code licensed under the BSD License: 13250http://developer.yahoo.com/yui/license.html 13251version: 2.9.0 13252*/ 13253/** 13254 * The Connection Manager provides a simplified interface to the XMLHttpRequest 13255 * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the 13256 * interactive states and server response, returning the results to a pre-defined 13257 * callback you create. 13258 * 13259 * @namespace YAHOO.util 13260 * @module connection 13261 * @requires yahoo 13262 * @requires event 13263 */ 13264 13265/** 13266 * The Connection Manager singleton provides methods for creating and managing 13267 * asynchronous transactions. 13268 * 13269 * @class YAHOO.util.Connect 13270 */ 13271 13272YAHOO.util.Connect = 13273{ 13274 /** 13275 * @description Array of MSFT ActiveX ids for XMLHttpRequest. 13276 * @property _msxml_progid 13277 * @private 13278 * @static 13279 * @type array 13280 */ 13281 _msxml_progid:[ 13282 'Microsoft.XMLHTTP', 13283 'MSXML2.XMLHTTP.3.0', 13284 'MSXML2.XMLHTTP' 13285 ], 13286 13287 /** 13288 * @description Object literal of HTTP header(s) 13289 * @property _http_header 13290 * @private 13291 * @static 13292 * @type object 13293 */ 13294 _http_headers:{}, 13295 13296 /** 13297 * @description Determines if HTTP headers are set. 13298 * @property _has_http_headers 13299 * @private 13300 * @static 13301 * @type boolean 13302 */ 13303 _has_http_headers:false, 13304 13305 /** 13306 * @description Determines if a default header of 13307 * Content-Type of 'application/x-www-form-urlencoded' 13308 * will be added to any client HTTP headers sent for POST 13309 * transactions. 13310 * @property _use_default_post_header 13311 * @private 13312 * @static 13313 * @type boolean 13314 */ 13315 _use_default_post_header:true, 13316 13317 /** 13318 * @description The default header used for POST transactions. 13319 * @property _default_post_header 13320 * @private 13321 * @static 13322 * @type boolean 13323 */ 13324 _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8', 13325 13326 /** 13327 * @description The default header used for transactions involving the 13328 * use of HTML forms. 13329 * @property _default_form_header 13330 * @private 13331 * @static 13332 * @type boolean 13333 */ 13334 _default_form_header:'application/x-www-form-urlencoded', 13335 13336 /** 13337 * @description Determines if a default header of 13338 * 'X-Requested-With: XMLHttpRequest' 13339 * will be added to each transaction. 13340 * @property _use_default_xhr_header 13341 * @private 13342 * @static 13343 * @type boolean 13344 */ 13345 _use_default_xhr_header:true, 13346 13347 /** 13348 * @description The default header value for the label 13349 * "X-Requested-With". This is sent with each 13350 * transaction, by default, to identify the 13351 * request as being made by YUI Connection Manager. 13352 * @property _default_xhr_header 13353 * @private 13354 * @static 13355 * @type boolean 13356 */ 13357 _default_xhr_header:'XMLHttpRequest', 13358 13359 /** 13360 * @description Determines if custom, default headers 13361 * are set for each transaction. 13362 * @property _has_default_header 13363 * @private 13364 * @static 13365 * @type boolean 13366 */ 13367 _has_default_headers:true, 13368 13369 /** 13370 * @description Property modified by setForm() to determine if the data 13371 * should be submitted as an HTML form. 13372 * @property _isFormSubmit 13373 * @private 13374 * @static 13375 * @type boolean 13376 */ 13377 _isFormSubmit:false, 13378 13379 /** 13380 * @description Determines if custom, default headers 13381 * are set for each transaction. 13382 * @property _has_default_header 13383 * @private 13384 * @static 13385 * @type boolean 13386 */ 13387 _default_headers:{}, 13388 13389 /** 13390 * @description Collection of polling references to the polling mechanism in handleReadyState. 13391 * @property _poll 13392 * @private 13393 * @static 13394 * @type object 13395 */ 13396 _poll:{}, 13397 13398 /** 13399 * @description Queue of timeout values for each transaction callback with a defined timeout value. 13400 * @property _timeOut 13401 * @private 13402 * @static 13403 * @type object 13404 */ 13405 _timeOut:{}, 13406 13407 /** 13408 * @description The polling frequency, in milliseconds, for HandleReadyState. 13409 * when attempting to determine a transaction's XHR readyState. 13410 * The default is 50 milliseconds. 13411 * @property _polling_interval 13412 * @private 13413 * @static 13414 * @type int 13415 */ 13416 _polling_interval:50, 13417 13418 /** 13419 * @description A transaction counter that increments the transaction id for each transaction. 13420 * @property _transaction_id 13421 * @private 13422 * @static 13423 * @type int 13424 */ 13425 _transaction_id:0, 13426 13427 /** 13428 * @description Custom event that fires at the start of a transaction 13429 * @property startEvent 13430 * @private 13431 * @static 13432 * @type CustomEvent 13433 */ 13434 startEvent: new YAHOO.util.CustomEvent('start'), 13435 13436 /** 13437 * @description Custom event that fires when a transaction response has completed. 13438 * @property completeEvent 13439 * @private 13440 * @static 13441 * @type CustomEvent 13442 */ 13443 completeEvent: new YAHOO.util.CustomEvent('complete'), 13444 13445 /** 13446 * @description Custom event that fires when handleTransactionResponse() determines a 13447 * response in the HTTP 2xx range. 13448 * @property successEvent 13449 * @private 13450 * @static 13451 * @type CustomEvent 13452 */ 13453 successEvent: new YAHOO.util.CustomEvent('success'), 13454 13455 /** 13456 * @description Custom event that fires when handleTransactionResponse() determines a 13457 * response in the HTTP 4xx/5xx range. 13458 * @property failureEvent 13459 * @private 13460 * @static 13461 * @type CustomEvent 13462 */ 13463 failureEvent: new YAHOO.util.CustomEvent('failure'), 13464 13465 /** 13466 * @description Custom event that fires when a transaction is successfully aborted. 13467 * @property abortEvent 13468 * @private 13469 * @static 13470 * @type CustomEvent 13471 */ 13472 abortEvent: new YAHOO.util.CustomEvent('abort'), 13473 13474 /** 13475 * @description A reference table that maps callback custom events members to its specific 13476 * event name. 13477 * @property _customEvents 13478 * @private 13479 * @static 13480 * @type object 13481 */ 13482 _customEvents: 13483 { 13484 onStart:['startEvent', 'start'], 13485 onComplete:['completeEvent', 'complete'], 13486 onSuccess:['successEvent', 'success'], 13487 onFailure:['failureEvent', 'failure'], 13488 onUpload:['uploadEvent', 'upload'], 13489 onAbort:['abortEvent', 'abort'] 13490 }, 13491 13492 /** 13493 * @description Member to add an ActiveX id to the existing xml_progid array. 13494 * In the event(unlikely) a new ActiveX id is introduced, it can be added
13495 * without internal code modifications. 13496 * @method setProgId 13497 * @public 13498 * @static 13499 * @param {string} id The ActiveX id to be added to initialize the XHR object. 13500 * @return void 13501 */ 13502 setProgId:function(id) 13503 { 13504 this._msxml_progid.unshift(id); 13505 YAHOO.log('ActiveX Program Id ' + id + ' added to _msxml_progid.', 'info', 'Connection'); 13506 }, 13507 13508 /** 13509 * @description Member to override the default POST header. 13510 * @method setDefaultPostHeader 13511 * @public 13512 * @static 13513 * @param {boolean} b Set and use default header - true or false . 13514 * @return void 13515 */ 13516 setDefaultPostHeader:function(b) 13517 { 13518 if(typeof b == 'string'){ 13519 this._default_post_header = b; 13520 this._use_default_post_header = true; 13521 13522 YAHOO.log('Default POST header set to ' + b, 'info', 'Connection'); 13523 } 13524 else if(typeof b == 'boolean'){ 13525 this._use_default_post_header = b; 13526 } 13527 }, 13528 13529 /** 13530 * @description Member to override the default transaction header.. 13531 * @method setDefaultXhrHeader 13532 * @public 13533 * @static 13534 * @param {boolean} b Set and use default header - true or false . 13535 * @return void 13536 */ 13537 setDefaultXhrHeader:function(b) 13538 { 13539 if(typeof b == 'string'){ 13540 this._default_xhr_header = b; 13541 YAHOO.log('Default XHR header set to ' + b, 'info', 'Connection'); 13542 } 13543 else{ 13544 this._use_default_xhr_header = b; 13545 } 13546 }, 13547 13548 /** 13549 * @description Member to modify the default polling interval. 13550 * @method setPollingInterval 13551 * @public 13552 * @static 13553 * @param {int} i The polling interval in milliseconds. 13554 * @return void 13555 */ 13556 setPollingInterval:function(i) 13557 { 13558 if(typeof i == 'number' && isFinite(i)){ 13559 this._polling_interval = i; 13560 YAHOO.log('Default polling interval set to ' + i +'ms', 'info', 'Connection'); 13561 } 13562 }, 13563 13564 /** 13565 * @description Instantiates a XMLHttpRequest object and returns an object with two properties: 13566 * the XMLHttpRequest instance and the transaction id. 13567 * @method createXhrObject 13568 * @private 13569 * @static 13570 * @param {int} transactionId Property containing the transaction id for this transaction. 13571 * @return object 13572 */ 13573 createXhrObject:function(transactionId) 13574 { 13575 var obj,http,i; 13576 try 13577 { 13578 // Instantiates XMLHttpRequest in non-IE browsers and assigns to http. 13579 http = new XMLHttpRequest(); 13580 // Object literal with http and tId properties 13581 obj = { conn:http, tId:transactionId, xhr: true }; 13582 YAHOO.log('XHR object created for transaction ' + transactionId, 'info', 'Connection'); 13583 } 13584 catch(e) 13585 { 13586 for(i=0; i<this._msxml_progid.length; ++i){ 13587 try 13588 { 13589 // Instantiates XMLHttpRequest for IE and assign to http 13590 http = new ActiveXObject(this._msxml_progid[i]); 13591 // Object literal with conn and tId properties 13592 obj = { conn:http, tId:transactionId, xhr: true }; 13593 YAHOO.log('ActiveX XHR object created for transaction ' + transactionId, 'info', 'Connection'); 13594 break; 13595 } 13596 catch(e1){} 13597 } 13598 } 13599 finally 13600 { 13601 return obj; 13602 } 13603 }, 13604 13605 /** 13606 * @description This method is called by asyncRequest to create a 13607 * valid connection object for the transaction. It also passes a 13608 * transaction id and increments the transaction id counter. 13609 * @method getConnectionObject 13610 * @private 13611 * @static 13612 * @return {object} 13613 */ 13614 getConnectionObject:function(t) 13615 { 13616 var o, tId = this._transaction_id; 13617 13618 try 13619 { 13620 if(!t){ 13621 o = this.createXhrObject(tId); 13622 } 13623 else{ 13624 o = {tId:tId}; 13625 if(t==='xdr'){ 13626 o.conn = this._transport; 13627 o.xdr = true; 13628 } 13629 else if(t==='upload'){ 13630 o.upload = true; 13631 } 13632 } 13633 13634 if(o){ 13635 this._transaction_id++; 13636 } 13637 } 13638 catch(e){} 13639 return o; 13640 }, 13641 13642 /** 13643 * @description Method for initiating an asynchronous request via the XHR object. 13644 * @method asyncRequest 13645 * @public 13646 * @static 13647 * @param {string} method HTTP transaction method 13648 * @param {string} uri Fully qualified path of resource 13649 * @param {callback} callback User-defined callback function or object 13650 * @param {string} postData POST body 13651 * @return {object} Returns the connection object 13652 */ 13653 asyncRequest:function(method, uri, callback, postData) 13654 { 13655 var args = callback&&callback.argument?callback.argument:null, 13656 YCM = this, 13657 o, t; 13658 13659 if(this._isFileUpload){ 13660 t = 'upload'; 13661 } 13662 else if(callback && callback.xdr){ 13663 t = 'xdr'; 13664 } 13665 13666 o = this.getConnectionObject(t); 13667 if(!o){ 13668 YAHOO.log('Unable to create connection object.', 'error', 'Connection'); 13669 return null; 13670 } 13671 else{ 13672 13673 // Intialize any transaction-specific custom events, if provided. 13674 if(callback && callback.customevents){ 13675 this.initCustomEvents(o, callback); 13676 } 13677 13678 if(this._isFormSubmit){ 13679 if(this._isFileUpload){ 13680 window.setTimeout(function(){YCM.uploadFile(o, callback, uri, postData);}, 10); 13681 return o; 13682 } 13683 13684 // If the specified HTTP method is GET, setForm() will return an 13685 // encoded string that is concatenated to the uri to 13686 // create a querystring. 13687 if(method.toUpperCase() == 'GET'){ 13688 if(this._sFormData.length !== 0){ 13689 // If the URI already contains a querystring, append an ampersand 13690 // and then concatenate _sFormData to the URI. 13691 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData; 13692 } 13693 } 13694 else if(method.toUpperCase() == 'POST'){ 13695 // If POST data exist in addition to the HTML form data, 13696 // it will be concatenated to the form data. 13697 postData = postData?this._sFormData + "&" + postData:this._sFormData; 13698 } 13699 } 13700 13701 if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){ 13702 // If callback.cache is defined and set to false, a 13703 // timestamp value will be added to the querystring. 13704 uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString(); 13705 } 13706 13707 // Each transaction will automatically include a custom header of
13708 // "X-Requested-With: XMLHttpRequest" to identify the request as 13709 // having originated from Connection Manager. 13710 if(this._use_default_xhr_header){ 13711 if(!this._default_headers['X-Requested-With']){ 13712 this.initHeader('X-Requested-With', this._default_xhr_header, true); 13713 YAHOO.log('Initialize transaction header X-Request-Header to XMLHttpRequest.', 'info', 'Connection'); 13714 } 13715 } 13716 13717 //If the transaction method is POST and the POST header value is set to true 13718 //or a custom value, initalize the Content-Type header to this value. 13719 if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){ 13720 this.initHeader('Content-Type', this._default_post_header); 13721 YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded; UTF-8 for POST transaction.', 'info', 'Connection'); 13722 } 13723 13724 if(o.xdr){ 13725 this.xdr(o, method, uri, callback, postData); 13726 return o; 13727 } 13728 13729 o.conn.open(method, uri, true); 13730 //Initialize all default and custom HTTP headers, 13731 if(this._has_default_headers || this._has_http_headers){ 13732 this.setHeader(o); 13733 } 13734 13735 this.handleReadyState(o, callback); 13736 o.conn.send(postData || ''); 13737 YAHOO.log('Transaction ' + o.tId + ' sent.', 'info', 'Connection'); 13738 13739 // Reset the HTML form data and state properties as 13740 // soon as the data are submitted. 13741 if(this._isFormSubmit === true){ 13742 this.resetFormState(); 13743 } 13744 13745 // Fire global custom event -- startEvent 13746 this.startEvent.fire(o, args); 13747 13748 if(o.startEvent){ 13749 // Fire transaction custom event -- startEvent 13750 o.startEvent.fire(o, args); 13751 } 13752 13753 return o; 13754 } 13755 }, 13756 13757 /** 13758 * @description This method creates and subscribes custom events, 13759 * specific to each transaction 13760 * @method initCustomEvents 13761 * @private 13762 * @static 13763 * @param {object} o The connection object 13764 * @param {callback} callback The user-defined callback object 13765 * @return {void} 13766 */ 13767 initCustomEvents:function(o, callback) 13768 { 13769 var prop; 13770 // Enumerate through callback.customevents members and bind/subscribe 13771 // events that match in the _customEvents table. 13772 for(prop in callback.customevents){ 13773 if(this._customEvents[prop][0]){ 13774 // Create the custom event 13775 o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null); 13776 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' created.', 'info', 'Connection'); 13777 13778 // Subscribe the custom event 13779 o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]); 13780 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' subscribed.', 'info', 'Connection'); 13781 } 13782 } 13783 }, 13784 13785 /** 13786 * @description This method serves as a timer that polls the XHR object's readyState 13787 * property during a transaction, instead of binding a callback to the 13788 * onreadystatechange event. Upon readyState 4, handleTransactionResponse 13789 * will process the response, and the timer will be cleared. 13790 * @method handleReadyState 13791 * @private 13792 * @static 13793 * @param {object} o The connection object 13794 * @param {callback} callback The user-defined callback object 13795 * @return {void} 13796 */ 13797 13798 handleReadyState:function(o, callback) 13799 13800 { 13801 var oConn = this, 13802 args = (callback && callback.argument)?callback.argument:null; 13803 13804 if(callback && callback.timeout){ 13805 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout); 13806 } 13807 13808 this._poll[o.tId] = window.setInterval( 13809 function(){ 13810 if(o.conn && o.conn.readyState === 4){ 13811 13812 // Clear the polling interval for the transaction 13813 // and remove the reference from _poll. 13814 window.clearInterval(oConn._poll[o.tId]); 13815 delete oConn._poll[o.tId]; 13816 13817 if(callback && callback.timeout){ 13818 window.clearTimeout(oConn._timeOut[o.tId]); 13819 delete oConn._timeOut[o.tId]; 13820 } 13821 13822 // Fire global custom event -- completeEvent 13823 oConn.completeEvent.fire(o, args); 13824 13825 if(o.completeEvent){ 13826 // Fire transaction custom event -- completeEvent 13827 o.completeEvent.fire(o, args); 13828 } 13829 13830 oConn.handleTransactionResponse(o, callback); 13831 } 13832 } 13833 ,this._polling_interval); 13834 }, 13835 13836 /** 13837 * @description This method attempts to interpret the server response and 13838 * determine whether the transaction was successful, or if an error or 13839 * exception was encountered. 13840 * @method handleTransactionResponse 13841 * @private 13842 * @static 13843 * @param {object} o The connection object 13844 * @param {object} callback The user-defined callback object 13845 * @param {boolean} isAbort Determines if the transaction was terminated via abort(). 13846 * @return {void} 13847 */ 13848 handleTransactionResponse:function(o, callback, isAbort) 13849 { 13850 var httpStatus, responseObject, 13851 args = (callback && callback.argument)?callback.argument:null, 13852 xdrS = (o.r && o.r.statusText === 'xdr:success')?true:false, 13853 xdrF = (o.r && o.r.statusText === 'xdr:failure')?true:false, 13854 xdrA = isAbort; 13855 13856 try 13857 { 13858 if((o.conn.status !== undefined && o.conn.status !== 0) || xdrS){ 13859 // XDR requests will not have HTTP status defined. The 13860 // statusText property will define the response status 13861 // set by the Flash transport. 13862 httpStatus = o.conn.status; 13863 } 13864 else if(xdrF && !xdrA){ 13865 // Set XDR transaction failure to a status of 0, which 13866 // resolves as an HTTP failure, instead of an exception. 13867 httpStatus = 0; 13868 } 13869 else{ 13870 httpStatus = 13030; 13871 } 13872 } 13873 catch(e){ 13874 13875 // 13030 is a custom code to indicate the condition -- in Mozilla/FF -- 13876 // when the XHR object's status and statusText properties are 13877 // unavailable, and a query attempt throws an exception. 13878 httpStatus = 13030; 13879 } 13880 13881 if((httpStatus >= 200 && httpStatus < 300) || httpStatus === 1223 || xdrS){ 13882 responseObject = o.xdr ? o.r : this.createResponseObject(o, args); 13883 if(callback && callback.success){ 13884 if(!callback.scope){ 13885 callback.success(responseObject); 13886 YAHOO.log('Success callback. HTTP code is ' + httpStatus, 'info', 'Connection'); 13887 } 13888 else{ 13889 // If a scope property is defined, the callback will be fired from 13890 // the context of the object. 13891 callback.success.apply(callback.scope, [responseObject]); 13892 YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection'); 13893 } 13894 } 13895 13896 // Fire global custom event -- successEvent 13897 this.successEvent.fire(responseObject); 13898 13899 if(o.successEvent){ 13900 // Fire transaction custom event -- successEvent 13901 o.successEvent.fire(responseObject); 13902 } 13903 } 13904 else{ 13905 switch(httpStatus){ 13906 // The following cases are wininet.dll error codes that may be encountered. 13907 case 12002: // Server timeout 13908 case 12029: // 12029 to 12031 correspond to dropped connections. 13909 case 12030: 13910 case 12031: 13911 case 12152: // Connection closed by server. 13912 case 13030: // See above comments for variable status. 13913 // XDR transactions will not resolve to this case, since the 13914 // response object is already built in the xdr response. 13915 responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false)); 13916 if(callback && callback.failure){ 13917 if(!callback.scope){ 13918 callback.failure(responseObject);
13919 YAHOO.log('Failure callback. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection'); 13920 } 13921 else{ 13922 callback.failure.apply(callback.scope, [responseObject]); 13923 YAHOO.log('Failure callback with scope. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection'); 13924 } 13925 } 13926 13927 break; 13928 default: 13929 responseObject = (o.xdr) ? o.response : this.createResponseObject(o, args); 13930 if(callback && callback.failure){ 13931 if(!callback.scope){ 13932 callback.failure(responseObject); 13933 YAHOO.log('Failure callback. HTTP status code is ' + httpStatus, 'warn', 'Connection'); 13934 } 13935 else{ 13936 callback.failure.apply(callback.scope, [responseObject]); 13937 YAHOO.log('Failure callback with scope. HTTP status code is ' + httpStatus, 'warn', 'Connection'); 13938 } 13939 } 13940 } 13941 13942 // Fire global custom event -- failureEvent 13943 this.failureEvent.fire(responseObject); 13944 13945 if(o.failureEvent){ 13946 // Fire transaction custom event -- failureEvent 13947 o.failureEvent.fire(responseObject); 13948 } 13949 13950 } 13951 13952 this.releaseObject(o); 13953 responseObject = null; 13954 }, 13955 13956 /** 13957 * @description This method evaluates the server response, creates and returns the results via 13958 * its properties. Success and failure cases will differ in the response 13959 * object's property values. 13960 * @method createResponseObject 13961 * @private 13962 * @static 13963 * @param {object} o The connection object 13964 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback 13965 * @return {object} 13966 */ 13967 createResponseObject:function(o, callbackArg) 13968 { 13969 var obj = {}, headerObj = {}, 13970 i, headerStr, header, delimitPos; 13971 13972 try 13973 { 13974 headerStr = o.conn.getAllResponseHeaders(); 13975 header = headerStr.split('\n'); 13976 for(i=0; i<header.length; i++){ 13977 delimitPos = header[i].indexOf(':'); 13978 if(delimitPos != -1){ 13979 headerObj[header[i].substring(0,delimitPos)] = YAHOO.lang.trim(header[i].substring(delimitPos+2)); 13980 } 13981 } 13982 } 13983 catch(e){} 13984 13985 obj.tId = o.tId; 13986 // Normalize IE's response to HTTP 204 when Win error 1223. 13987 obj.status = (o.conn.status == 1223)?204:o.conn.status; 13988 // Normalize IE's statusText to "No Content" instead of "Unknown". 13989 obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText; 13990 obj.getResponseHeader = headerObj; 13991 obj.getAllResponseHeaders = headerStr; 13992 obj.responseText = o.conn.responseText; 13993 obj.responseXML = o.conn.responseXML; 13994 13995 if(callbackArg){ 13996 obj.argument = callbackArg; 13997 } 13998 13999 return obj; 14000 }, 14001 14002 /** 14003 * @description If a transaction cannot be completed due to dropped or closed connections, 14004 * there may be not be enough information to build a full response object. 14005 * The failure callback will be fired and this specific condition can be identified 14006 * by a status property value of 0. 14007 * 14008 * If an abort was successful, the status property will report a value of -1. 14009 * 14010 * @method createExceptionObject 14011 * @private 14012 * @static 14013 * @param {int} tId The Transaction Id 14014 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback 14015 * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort 14016 * @return {object} 14017 */ 14018 createExceptionObject:function(tId, callbackArg, isAbort) 14019 { 14020 var COMM_CODE = 0, 14021 COMM_ERROR = 'communication failure', 14022 ABORT_CODE = -1, 14023 ABORT_ERROR = 'transaction aborted', 14024 obj = {}; 14025 14026 obj.tId = tId; 14027 if(isAbort){ 14028 obj.status = ABORT_CODE; 14029 obj.statusText = ABORT_ERROR; 14030 } 14031 else{ 14032 obj.status = COMM_CODE; 14033 obj.statusText = COMM_ERROR; 14034 } 14035 14036 if(callbackArg){ 14037 obj.argument = callbackArg; 14038 } 14039 14040 return obj; 14041 }, 14042 14043 /** 14044 * @description Method that initializes the custom HTTP headers for the each transaction. 14045 * @method initHeader 14046 * @public 14047 * @static 14048 * @param {string} label The HTTP header label 14049 * @param {string} value The HTTP header value 14050 * @param {string} isDefault Determines if the specific header is a default header 14051 * automatically sent with each transaction. 14052 * @return {void} 14053 */ 14054 initHeader:function(label, value, isDefault) 14055 { 14056 var headerObj = (isDefault)?this._default_headers:this._http_headers; 14057 14058 headerObj[label] = value; 14059 if(isDefault){ 14060 this._has_default_headers = true; 14061 } 14062 else{ 14063 this._has_http_headers = true; 14064 } 14065 }, 14066 14067 14068 /** 14069 * @description Accessor that sets the HTTP headers for each transaction. 14070 * @method setHeader 14071 * @private 14072 * @static 14073 * @param {object} o The connection object for the transaction. 14074 * @return {void} 14075 */ 14076 setHeader:function(o) 14077 { 14078 var prop; 14079 if(this._has_default_headers){ 14080 for(prop in this._default_headers){ 14081 if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){ 14082 o.conn.setRequestHeader(prop, this._default_headers[prop]); 14083 YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection'); 14084 } 14085 } 14086 } 14087 14088 if(this._has_http_headers){ 14089 for(prop in this._http_headers){ 14090 if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){ 14091 o.conn.setRequestHeader(prop, this._http_headers[prop]); 14092 YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection'); 14093 } 14094 } 14095 14096 this._http_headers = {}; 14097 this._has_http_headers = false;
14098 } 14099 }, 14100 14101 /** 14102 * @description Resets the default HTTP headers object 14103 * @method resetDefaultHeaders 14104 * @public 14105 * @static 14106 * @return {void} 14107 */ 14108 resetDefaultHeaders:function(){ 14109 this._default_headers = {}; 14110 this._has_default_headers = false; 14111 }, 14112 14113 /** 14114 * @description Method to terminate a transaction, if it has not reached readyState 4. 14115 * @method abort 14116 * @public 14117 * @static 14118 * @param {object} o The connection object returned by asyncRequest. 14119 * @param {object} callback User-defined callback object. 14120 * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout. 14121 * @return {boolean} 14122 */ 14123 abort:function(o, callback, isTimeout) 14124 { 14125 var abortStatus, 14126 args = (callback && callback.argument)?callback.argument:null; 14127 o = o || {}; 14128 14129 if(o.conn){ 14130 if(o.xhr){ 14131 if(this.isCallInProgress(o)){ 14132 // Issue abort request 14133 o.conn.abort(); 14134 14135 window.clearInterval(this._poll[o.tId]); 14136 delete this._poll[o.tId]; 14137 14138 if(isTimeout){ 14139 window.clearTimeout(this._timeOut[o.tId]); 14140 delete this._timeOut[o.tId]; 14141 } 14142 14143 abortStatus = true; 14144 } 14145 } 14146 else if(o.xdr){ 14147 o.conn.abort(o.tId); 14148 abortStatus = true; 14149 } 14150 } 14151 else if(o.upload){ 14152 var frameId = 'yuiIO' + o.tId; 14153 var io = document.getElementById(frameId); 14154 14155 if(io){ 14156 // Remove all listeners on the iframe prior to 14157 // its destruction. 14158 YAHOO.util.Event.removeListener(io, "load"); 14159 // Destroy the iframe facilitating the transaction. 14160 document.body.removeChild(io); 14161 YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection'); 14162 14163 if(isTimeout){ 14164 window.clearTimeout(this._timeOut[o.tId]); 14165 delete this._timeOut[o.tId]; 14166 } 14167 14168 abortStatus = true; 14169 } 14170 } 14171 else{ 14172 abortStatus = false; 14173 } 14174 14175 if(abortStatus === true){ 14176 // Fire global custom event -- abortEvent 14177 this.abortEvent.fire(o, args); 14178 14179 if(o.abortEvent){ 14180 // Fire transaction custom event -- abortEvent 14181 o.abortEvent.fire(o, args); 14182 } 14183 14184 this.handleTransactionResponse(o, callback, true); 14185 YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection'); 14186 } 14187 14188 return abortStatus; 14189 }, 14190 14191 /** 14192 * @description Determines if the transaction is still being processed. 14193 * @method isCallInProgress 14194 * @public 14195 * @static 14196 * @param {object} o The connection object returned by asyncRequest 14197 * @return {boolean} 14198 */ 14199 isCallInProgress:function(o) 14200 { 14201 o = o || {}; 14202 // if the XHR object assigned to the transaction has not been dereferenced, 14203 // then check its readyState status. Otherwise, return false. 14204 if(o.xhr && o.conn){ 14205 return o.conn.readyState !== 4 && o.conn.readyState !== 0; 14206 } 14207 else if(o.xdr && o.conn){ 14208 return o.conn.isCallInProgress(o.tId); 14209 } 14210 else if(o.upload === true){ 14211 return document.getElementById('yuiIO' + o.tId)?true:false; 14212 } 14213 else{ 14214 return false; 14215 } 14216 }, 14217 14218 /** 14219 * @description Dereference the XHR instance and the connection object after the transaction is completed. 14220 * @method releaseObject 14221 * @private 14222 * @static 14223 * @param {object} o The connection object 14224 * @return {void} 14225 */ 14226 releaseObject:function(o) 14227 { 14228 if(o && o.conn){ 14229 //dereference the XHR instance. 14230 o.conn = null; 14231 14232 YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection'); 14233 14234 //dereference the connection object. 14235 o = null; 14236 } 14237 } 14238}; 14239 14240/** 14241 * @for YAHOO.util.Connect 14242 */ 14243(function() { 14244 var YCM = YAHOO.util.Connect, _fn = {}; 14245 14246 /**
14247 * @description This method creates and instantiates the Flash transport. 14248 * @method _swf 14249 * @private 14250 * @static 14251 * @param {string} URI to connection.swf. 14252 * @return {void} 14253 */ 14254 function _swf(uri) { 14255 var o = '<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="' + 14256 uri + '" width="0" height="0">' + 14257 '<param name="movie" value="' + uri + '">' + 14258 '<param name="allowScriptAccess" value="always">' + 14259 '</object>', 14260 c = document.createElement('div'); 14261 14262 document.body.appendChild(c); 14263 c.innerHTML = o; 14264 } 14265 14266 /** 14267 * @description This method calls the public method on the 14268 * Flash transport to start the XDR transaction. It is analogous 14269 * to Connection Manager's asyncRequest method. 14270 * @method xdr 14271 * @private 14272 * @static 14273 * @param {object} The transaction object. 14274 * @param {string} HTTP request method. 14275 * @param {string} URI for the transaction. 14276 * @param {object} The transaction's callback object. 14277 * @param {object} The JSON object used as HTTP POST data. 14278 * @return {void} 14279 */ 14280 function _xdr(o, m, u, c, d) { 14281 _fn[parseInt(o.tId)] = { 'o':o, 'c':c }; 14282 if (d) { 14283 c.method = m; 14284 c.data = d; 14285 } 14286 14287 o.conn.send(u, c, o.tId); 14288 } 14289 14290 /** 14291 * @description This method instantiates the Flash transport and 14292 * establishes a static reference to it, used for all XDR requests. 14293 * @method transport 14294 * @public 14295 * @static 14296 * @param {string} URI to connection.swf. 14297 * @return {void} 14298 */ 14299 function _init(uri) { 14300 _swf(uri); 14301 YCM._transport = document.getElementById('YUIConnectionSwf'); 14302 } 14303 14304 function _xdrReady() { 14305 YCM.xdrReadyEvent.fire(); 14306 } 14307 14308 /** 14309 * @description This method fires the global and transaction start 14310 * events. 14311 * @method _xdrStart 14312 * @private 14313 * @static 14314 * @param {object} The transaction object. 14315 * @param {string} The transaction's callback object. 14316 * @return {void} 14317 */ 14318 function _xdrStart(o, cb) { 14319 if (o) { 14320 // Fire global custom event -- startEvent 14321 YCM.startEvent.fire(o, cb.argument); 14322 14323 if(o.startEvent){ 14324 // Fire transaction custom event -- startEvent 14325 o.startEvent.fire(o, cb.argument); 14326 } 14327 } 14328 } 14329 14330 /** 14331 * @description This method is the initial response handler 14332 * for XDR transactions. The Flash transport calls this 14333 * function and sends the response payload. 14334 * @method handleXdrResponse 14335 * @private 14336 * @static 14337 * @param {object} The response object sent from the Flash transport. 14338 * @return {void} 14339 */ 14340 function _handleXdrResponse(r) { 14341 var o = _fn[r.tId].o, 14342 cb = _fn[r.tId].c; 14343 14344 if (r.statusText === 'xdr:start') { 14345 _xdrStart(o, cb); 14346 return; 14347 } 14348 14349 r.responseText = decodeURI(r.responseText); 14350 o.r = r; 14351 if (cb.argument) { 14352 o.r.argument = cb.argument; 14353 } 14354 14355 this.handleTransactionResponse(o, cb, r.statusText === 'xdr:abort' ? true : false); 14356 delete _fn[r.tId]; 14357 } 14358 14359 // Bind the functions to Connection Manager as static fields. 14360 YCM.xdr = _xdr; 14361 YCM.swf = _swf; 14362 YCM.transport = _init; 14363 YCM.xdrReadyEvent = new YAHOO.util.CustomEvent('xdrReady'); 14364 YCM.xdrReady = _xdrReady; 14365 YCM.handleXdrResponse = _handleXdrResponse; 14366})(); 14367 14368/** 14369 * @for YAHOO.util.Connect 14370 */ 14371(function(){ 14372 var YCM = YAHOO.util.Connect, 14373 YE = YAHOO.util.Event, 14374 dM = document.documentMode ? document.documentMode : false; 14375 14376 /** 14377 * @description Property modified by setForm() to determine if a file(s) 14378 * upload is expected. 14379 * @property _isFileUpload 14380 * @private 14381 * @static 14382 * @type boolean 14383 */ 14384 YCM._isFileUpload = false; 14385 14386 /** 14387 * @description Property modified by setForm() to set a reference to the HTML 14388 * form node if the desired action is file upload. 14389 * @property _formNode 14390 * @private 14391 * @static 14392 * @type object 14393 */ 14394 YCM._formNode = null; 14395 14396 /** 14397 * @description Property modified by setForm() to set the HTML form data 14398 * for each transaction. 14399 * @property _sFormData 14400 * @private 14401 * @static 14402 * @type string 14403 */ 14404 YCM._sFormData = null; 14405 14406 /** 14407 * @description Tracks the name-value pair of the "clicked" submit button if multiple submit 14408 * buttons are present in an HTML form; and, if YAHOO.util.Event is available. 14409 * @property _submitElementValue 14410 * @private 14411 * @static 14412 * @type string 14413 */ 14414 YCM._submitElementValue = null; 14415 14416 /**
14417 * @description Custom event that fires when handleTransactionResponse() determines a 14418 * response in the HTTP 4xx/5xx range. 14419 * @property failureEvent 14420 * @private 14421 * @static 14422 * @type CustomEvent 14423 */ 14424 YCM.uploadEvent = new YAHOO.util.CustomEvent('upload'); 14425 14426 /** 14427 * @description Determines whether YAHOO.util.Event is available and returns true or false. 14428 * If true, an event listener is bound at the document level to trap click events that 14429 * resolve to a target type of "Submit". This listener will enable setForm() to determine 14430 * the clicked "Submit" value in a multi-Submit button, HTML form. 14431 * @property _hasSubmitListener 14432 * @private 14433 * @static 14434 */ 14435 YCM._hasSubmitListener = function() { 14436 if(YE){ 14437 YE.addListener( 14438 document, 14439 'click', 14440 function(e){ 14441 var obj = YE.getTarget(e), 14442 name = obj.nodeName.toLowerCase(); 14443 14444 if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){ 14445 YCM._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value); 14446 } 14447 }); 14448 return true; 14449 } 14450 return false; 14451 }(); 14452 14453 /** 14454 * @description This method assembles the form label and value pairs and 14455 * constructs an encoded string. 14456 * asyncRequest() will automatically initialize the transaction with a 14457 * a HTTP header Content-Type of application/x-www-form-urlencoded. 14458 * @method setForm 14459 * @public 14460 * @static 14461 * @param {string || object} form id or name attribute, or form object. 14462 * @param {boolean} optional enable file upload. 14463 * @param {boolean} optional enable file upload over SSL in IE only. 14464 * @return {string} string of the HTML form field name and value pairs.. 14465 */ 14466 function _setForm(formId, isUpload, secureUri) 14467 { 14468 var oForm, oElement, oName, oValue, oDisabled, 14469 hasSubmit = false, 14470 data = [], item = 0, 14471 i,len,j,jlen,opt; 14472 14473 this.resetFormState(); 14474 14475 if(typeof formId == 'string'){ 14476 // Determine if the argument is a form id or a form name. 14477 // Note form name usage is deprecated by supported 14478 // here for legacy reasons. 14479 oForm = (document.getElementById(formId) || document.forms[formId]); 14480 } 14481 else if(typeof formId == 'object'){ 14482 // Treat argument as an HTML form object. 14483 oForm = formId; 14484 } 14485 else{ 14486 YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection'); 14487 return; 14488 } 14489 14490 // If the isUpload argument is true, setForm will call createFrame to initialize 14491 // an iframe as the form target. 14492 // 14493 // The argument secureURI is also required by IE in SSL environments 14494 // where the secureURI string is a fully qualified HTTP path, used to set the source 14495 // of the iframe, to a stub resource in the same domain. 14496 if(isUpload){ 14497 14498 // Create iframe in preparation for file upload. 14499 this.createFrame(secureUri?secureUri:null); 14500 14501 // Set form reference and file upload properties to true. 14502 this._isFormSubmit = true; 14503 this._isFileUpload = true; 14504 this._formNode = oForm; 14505 14506 return; 14507 } 14508 14509 // Iterate over the form elements collection to construct the 14510 // label-value pairs. 14511 for (i=0,len=oForm.elements.length; i<len; ++i){ 14512 oElement = oForm.elements[i]; 14513 oDisabled = oElement.disabled; 14514 oName = oElement.name; 14515
14516 // Do not submit fields that are disabled or 14517 // do not have a name attribute value. 14518 if(!oDisabled && oName) 14519 { 14520 oName = encodeURIComponent(oName)+'='; 14521 oValue = encodeURIComponent(oElement.value); 14522 14523 switch(oElement.type) 14524 { 14525 // Safari, Opera, FF all default opt.value from .text if 14526 // value attribute not specified in markup 14527 case 'select-one': 14528 if (oElement.selectedIndex > -1) { 14529 opt = oElement.options[oElement.selectedIndex]; 14530 data[item++] = oName + encodeURIComponent( 14531 (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text); 14532 } 14533 break; 14534 case 'select-multiple': 14535 if (oElement.selectedIndex > -1) { 14536 for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){ 14537 opt = oElement.options[j]; 14538 if (opt.selected) { 14539 data[item++] = oName + encodeURIComponent( 14540 (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text); 14541 } 14542 } 14543 } 14544 break; 14545 case 'radio': 14546 case 'checkbox': 14547 if(oElement.checked){ 14548 data[item++] = oName + oValue; 14549 } 14550 break; 14551 case 'file': 14552 // stub case as XMLHttpRequest will only send the file path as a string. 14553 case undefined: 14554 // stub case for fieldset element which returns undefined. 14555 case 'reset': 14556 // stub case for input type reset button. 14557 case 'button': 14558 // stub case for input type button elements. 14559 break; 14560 case 'submit': 14561 if(hasSubmit === false){ 14562 if(this._hasSubmitListener && this._submitElementValue){ 14563 data[item++] = this._submitElementValue; 14564 } 14565 hasSubmit = true; 14566 } 14567 break; 14568 default: 14569 data[item++] = oName + oValue; 14570 } 14571 } 14572 } 14573 14574 this._isFormSubmit = true; 14575 this._sFormData = data.join('&'); 14576 14577 YAHOO.log('Form initialized for transaction. HTML form POST message is: ' + this._sFormData, 'info', 'Connection'); 14578 14579 this.initHeader('Content-Type', this._default_form_header); 14580 YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded for setForm() transaction.', 'info', 'Connection'); 14581 14582 return this._sFormData; 14583 } 14584 14585 /** 14586 * @description Resets HTML form properties when an HTML form or HTML form 14587 * with file upload transaction is sent. 14588 * @method resetFormState 14589 * @private 14590 * @static 14591 * @return {void} 14592 */ 14593 function _resetFormState(){ 14594 this._isFormSubmit = false; 14595 this._isFileUpload = false; 14596 this._formNode = null; 14597 this._sFormData = ""; 14598 } 14599 14600 14601 /** 14602 * @description Creates an iframe to be used for form file uploads. It is remove from the 14603 * document upon completion of the upload transaction. 14604 * @method createFrame 14605 * @private 14606 * @static 14607 * @param {string} optional qualified path of iframe resource for SSL in IE. 14608 * @return {void} 14609 */ 14610 function _createFrame(secureUri){ 14611 14612 // IE does not allow the setting of id and name attributes as object 14613 // properties via createElement(). A different iframe creation 14614 // pattern is required for IE. 14615 var frameId = 'yuiIO' + this._transaction_id, 14616 ie9 = (dM === 9) ? true : false, 14617 io; 14618 14619 if(YAHOO.env.ua.ie && !ie9){ 14620 io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />'); 14621 14622 // IE will throw a security exception in an SSL environment if the 14623 // iframe source is undefined. 14624 if(typeof secureUri == 'boolean'){ 14625 io.src = 'javascript:false'; 14626 } 14627 } 14628 else{ 14629 io = document.createElement('iframe'); 14630 io.id = frameId; 14631 io.name = frameId; 14632 } 14633 14634 io.style.position = 'absolute'; 14635 io.style.top = '-1000px'; 14636 io.style.left = '-1000px'; 14637 14638 document.body.appendChild(io); 14639 YAHOO.log('File upload iframe created. Id is:' + frameId, 'info', 'Connection'); 14640 } 14641 14642 /** 14643 * @description Parses the POST data and creates hidden form elements 14644 * for each key-value, and appends them to the HTML form object. 14645 * @method appendPostData 14646 * @private 14647 * @static 14648 * @param {string} postData The HTTP POST data 14649 * @return {array} formElements Collection of hidden fields. 14650 */ 14651 function _appendPostData(postData){ 14652 var formElements = [], 14653 postMessage = postData.split('&'), 14654 i, delimitPos; 14655 14656 for(i=0; i < postMessage.length; i++){ 14657 delimitPos = postMessage[i].indexOf('='); 14658 if(delimitPos != -1){ 14659 formElements[i] = document.createElement('input'); 14660 formElements[i].type = 'hidden'; 14661 formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos)); 14662 formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1)); 14663 this._formNode.appendChild(formElements[i]); 14664 } 14665 } 14666 14667 return formElements; 14668 } 14669 14670 /**
14671 * @description Uploads HTML form, inclusive of files/attachments, using the 14672 * iframe created in createFrame to facilitate the transaction. 14673 * @method uploadFile 14674 * @private 14675 * @static 14676 * @param {int} id The transaction id. 14677 * @param {object} callback User-defined callback object. 14678 * @param {string} uri Fully qualified path of resource. 14679 * @param {string} postData POST data to be submitted in addition to HTML form. 14680 * @return {void} 14681 */ 14682 function _uploadFile(o, callback, uri, postData){ 14683 // Each iframe has an id prefix of "yuiIO" followed 14684 // by the unique transaction id. 14685 var frameId = 'yuiIO' + o.tId, 14686 uploadEncoding = 'multipart/form-data', 14687 io = document.getElementById(frameId), 14688 ie8 = (dM >= 8) ? true : false, 14689 oConn = this, 14690 args = (callback && callback.argument)?callback.argument:null, 14691 oElements,i,prop,obj, rawFormAttributes, uploadCallback; 14692 14693 // Track original HTML form attribute values. 14694 rawFormAttributes = { 14695 action:this._formNode.getAttribute('action'), 14696 method:this._formNode.getAttribute('method'), 14697 target:this._formNode.getAttribute('target') 14698 }; 14699 14700 // Initialize the HTML form properties in case they are 14701 // not defined in the HTML form. 14702 this._formNode.setAttribute('action', uri); 14703 this._formNode.setAttribute('method', 'POST'); 14704 this._formNode.setAttribute('target', frameId); 14705 14706 if(YAHOO.env.ua.ie && !ie8){ 14707 // IE does not respect property enctype for HTML forms. 14708 // Instead it uses the property - "encoding". 14709 this._formNode.setAttribute('encoding', uploadEncoding); 14710 } 14711 else{ 14712 this._formNode.setAttribute('enctype', uploadEncoding); 14713 } 14714 14715 if(postData){ 14716 oElements = this.appendPostData(postData); 14717 } 14718 14719 // Start file upload. 14720 this._formNode.submit(); 14721 14722 // Fire global custom event -- startEvent 14723 this.startEvent.fire(o, args); 14724 14725 if(o.startEvent){ 14726 // Fire transaction custom event -- startEvent 14727 o.startEvent.fire(o, args); 14728 } 14729 14730 // Start polling if a callback is present and the timeout 14731 // property has been defined. 14732 if(callback && callback.timeout){ 14733 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout); 14734 } 14735 14736 // Remove HTML elements created by appendPostData 14737 if(oElements && oElements.length > 0){ 14738 for(i=0; i < oElements.length; i++){ 14739 this._formNode.removeChild(oElements[i]); 14740 } 14741 } 14742 14743 // Restore HTML form attributes to their original 14744 // values prior to file upload. 14745 for(prop in rawFormAttributes){ 14746 if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){ 14747 if(rawFormAttributes[prop]){ 14748 this._formNode.setAttribute(prop, rawFormAttributes[prop]); 14749 } 14750 else{ 14751 this._formNode.removeAttribute(prop); 14752 } 14753 } 14754 } 14755 14756 // Reset HTML form state properties. 14757 this.resetFormState(); 14758 14759 // Create the upload callback handler that fires when the iframe 14760 // receives the load event. Subsequently, the event handler is detached 14761 // and the iframe removed from the document. 14762 uploadCallback = function() { 14763 var body, pre, text; 14764 14765 if(callback && callback.timeout){ 14766 window.clearTimeout(oConn._timeOut[o.tId]); 14767 delete oConn._timeOut[o.tId]; 14768 } 14769 14770 // Fire global custom event -- completeEvent 14771 oConn.completeEvent.fire(o, args); 14772 14773 if(o.completeEvent){ 14774 // Fire transaction custom event -- completeEvent 14775 o.completeEvent.fire(o, args); 14776 } 14777 14778 obj = { 14779 tId : o.tId, 14780 argument : args 14781 }; 14782 14783 try 14784 { 14785 body = io.contentWindow.document.getElementsByTagName('body')[0]; 14786 pre = io.contentWindow.document.getElementsByTagName('pre')[0]; 14787 14788 if (body) { 14789 if (pre) { 14790 text = pre.textContent?pre.textContent:pre.innerText; 14791 } 14792 else { 14793 text = body.textContent?body.textContent:body.innerText; 14794 } 14795 } 14796 obj.responseText = text; 14797 // responseText and responseXML will be populated with the same data from the iframe. 14798 // Since the HTTP headers cannot be read from the iframe 14799 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document; 14800 } 14801 catch(e){} 14802 14803 if(callback && callback.upload){ 14804 if(!callback.scope){ 14805 callback.upload(obj); 14806 YAHOO.log('Upload callback.', 'info', 'Connection'); 14807 } 14808 else{ 14809 callback.upload.apply(callback.scope, [obj]); 14810 YAHOO.log('Upload callback with scope.', 'info', 'Connection'); 14811 } 14812 } 14813 14814 // Fire global custom event -- uploadEvent 14815 oConn.uploadEvent.fire(obj); 14816 14817 if(o.uploadEvent){ 14818 // Fire transaction custom event -- uploadEvent 14819 o.uploadEvent.fire(obj); 14820 } 14821 14822 YE.removeListener(io, "load", uploadCallback); 14823 14824 setTimeout( 14825 function(){ 14826 document.body.removeChild(io); 14827 oConn.releaseObject(o); 14828 YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection'); 14829 }, 100); 14830 }; 14831 14832 // Bind the onload handler to the iframe to detect the file upload response. 14833 YE.addListener(io, "load", uploadCallback); 14834 } 14835 14836 YCM.setForm = _setForm; 14837 YCM.resetFormState = _resetFormState; 14838 YCM.createFrame = _createFrame; 14839 YCM.appendPostData = _appendPostData; 14840 YCM.uploadFile = _uploadFile; 14841})(); 14842 14843YAHOO.register("connection", YAHOO.util.Connect, {version: "2.9.0", build: "2800"}); 14844/* 14845Copyright (c) 2011, Yahoo! Inc. All rights reserved. 14846Code licensed under the BSD License:
14847http://developer.yahoo.com/yui/license.html 14848version: 2.9.0 14849*/ 14850/** 14851 * Provides Attribute configurations. 14852 * @namespace YAHOO.util 14853 * @class Attribute 14854 * @constructor 14855 * @param hash {Object} The intial Attribute. 14856 * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance. 14857 */ 14858 14859YAHOO.util.Attribute = function(hash, owner) { 14860 if (owner) { 14861 this.owner = owner; 14862 this.configure(hash, true); 14863 } 14864}; 14865 14866YAHOO.util.Attribute.INVALID_VALUE = {}; 14867 14868YAHOO.util.Attribute.prototype = { 14869 /** 14870 * The name of the attribute. 14871 * @property name 14872 * @type String 14873 */ 14874 name: undefined, 14875 14876 /** 14877 * The value of the attribute. 14878 * @property value 14879 * @type String 14880 */ 14881 value: null, 14882 14883 /** 14884 * The owner of the attribute. 14885 * @property owner 14886 * @type YAHOO.util.AttributeProvider 14887 */ 14888 owner: null, 14889 14890 /** 14891 * Whether or not the attribute is read only. 14892 * @property readOnly 14893 * @type Boolean 14894 */ 14895 readOnly: false, 14896 14897 /** 14898 * Whether or not the attribute can only be written once. 14899 * @property writeOnce 14900 * @type Boolean 14901 */ 14902 writeOnce: false, 14903 14904 /** 14905 * The attribute's initial configuration. 14906 * @private 14907 * @property _initialConfig 14908 * @type Object 14909 */ 14910 _initialConfig: null, 14911 14912 /** 14913 * Whether or not the attribute's value has been set. 14914 * @private 14915 * @property _written 14916 * @type Boolean 14917 */ 14918 _written: false, 14919 14920 /** 14921 * A function to call when setting the attribute's value. 14922 * The method receives the new value as the first arg and the attribute name as the 2nd 14923 * @property method 14924 * @type Function 14925 */ 14926 method: null, 14927 14928 /** 14929 * The function to use when setting the attribute's value. 14930 * The setter receives the new value as the first arg and the attribute name as the 2nd 14931 * The return value of the setter replaces the value passed to set(). 14932 * @property setter 14933 * @type Function 14934 */ 14935 setter: null, 14936 14937 /** 14938 * The function to use when getting the attribute's value. 14939 * The getter receives the new value as the first arg and the attribute name as the 2nd 14940 * The return value of the getter will be used as the return from get(). 14941 * @property getter 14942 * @type Function 14943 */ 14944 getter: null, 14945 14946 /** 14947 * The validator to use when setting the attribute's value. 14948 * @property validator 14949 * @type Function 14950 * @return Boolean 14951 */ 14952 validator: null, 14953 14954 /** 14955 * Retrieves the current value of the attribute. 14956 * @method getValue 14957 * @return {any} The current value of the attribute. 14958 */ 14959 getValue: function() { 14960 var val = this.value; 14961 14962 if (this.getter) { 14963 val = this.getter.call(this.owner, this.name, val); 14964 } 14965 14966 return val; 14967 }, 14968 14969 /** 14970 * Sets the value of the attribute and fires beforeChange and change events. 14971 * @method setValue 14972 * @param {Any} value The value to apply to the attribute. 14973 * @param {Boolean} silent If true the change events will not be fired. 14974 * @return {Boolean} Whether or not the value was set. 14975 */ 14976 setValue: function(value, silent) { 14977 var beforeRetVal, 14978 owner = this.owner, 14979 name = this.name, 14980 invalidValue = YAHOO.util.Attribute.INVALID_VALUE, 14981 14982 event = { 14983 type: name, 14984 prevValue: this.getValue(), 14985 newValue: value 14986 }; 14987 14988 if (this.readOnly || ( this.writeOnce && this._written) ) { 14989 YAHOO.log( 'setValue ' + name + ', ' + value + 14990 ' failed: read only', 'error', 'Attribute'); 14991 return false; // write not allowed 14992 } 14993 14994 if (this.validator && !this.validator.call(owner, value) ) { 14995 YAHOO.log( 'setValue ' + name + ', ' + value + 14996 ' validation failed', 'error', 'Attribute'); 14997 return false; // invalid value 14998 } 14999 15000 if (!silent) { 15001 beforeRetVal = owner.fireBeforeChangeEvent(event); 15002 if (beforeRetVal === false) { 15003 YAHOO.log('setValue ' + name + 15004 ' cancelled by beforeChange event', 'info', 'Attribute'); 15005 return false;
15006 } 15007 } 15008 15009 if (this.setter) { 15010 value = this.setter.call(owner, value, this.name); 15011 if (value === undefined) { 15012 YAHOO.log('setter for ' + this.name + ' returned undefined', 'warn', 'Attribute'); 15013 } 15014 15015 if (value === invalidValue) { 15016 return false; 15017 } 15018 } 15019 15020 if (this.method) { 15021 if (this.method.call(owner, value, this.name) === invalidValue) { 15022 return false; 15023 } 15024 } 15025 15026 this.value = value; // TODO: set before calling setter/method? 15027 this._written = true; 15028 15029 event.type = name; 15030 15031 if (!silent) { 15032 this.owner.fireChangeEvent(event); 15033 } 15034 15035 return true; 15036 }, 15037 15038 /** 15039 * Allows for configuring the Attribute's properties. 15040 * @method configure 15041 * @param {Object} map A key-value map of Attribute properties. 15042 * @param {Boolean} init Whether or not this should become the initial config. 15043 */ 15044 configure: function(map, init) { 15045 map = map || {}; 15046 15047 if (init) { 15048 this._written = false; // reset writeOnce 15049 } 15050 15051 this._initialConfig = this._initialConfig || {}; 15052 15053 for (var key in map) { 15054 if ( map.hasOwnProperty(key) ) { 15055 this[key] = map[key]; 15056 if (init) { 15057 this._initialConfig[key] = map[key]; 15058 } 15059 } 15060 } 15061 }, 15062 15063 /** 15064 * Resets the value to the initial config value. 15065 * @method resetValue 15066 * @return {Boolean} Whether or not the value was set. 15067 */ 15068 resetValue: function() { 15069 return this.setValue(this._initialConfig.value); 15070 }, 15071 15072 /** 15073 * Resets the attribute config to the initial config state. 15074 * @method resetConfig 15075 */ 15076 resetConfig: function() { 15077 this.configure(this._initialConfig, true); 15078 }, 15079 15080 /** 15081 * Resets the value to the current value. 15082 * Useful when values may have gotten out of sync with actual properties. 15083 * @method refresh 15084 * @return {Boolean} Whether or not the value was set. 15085 */ 15086 refresh: function(silent) { 15087 this.setValue(this.value, silent); 15088 } 15089}; 15090 15091(function() { 15092 var Lang = YAHOO.util.Lang; 15093 15094 /* 15095 Copyright (c) 2006, Yahoo! Inc. All rights reserved. 15096 Code licensed under the BSD License: 15097 http://developer.yahoo.net/yui/license.txt 15098 */ 15099 15100 /** 15101 * Provides and manages YAHOO.util.Attribute instances 15102 * @namespace YAHOO.util 15103 * @class AttributeProvider 15104 * @uses YAHOO.util.EventProvider 15105 */ 15106 YAHOO.util.AttributeProvider = function() {}; 15107 15108 YAHOO.util.AttributeProvider.prototype = { 15109 15110 /** 15111 * A key-value map of Attribute configurations 15112 * @property _configs 15113 * @protected (may be used by subclasses and augmentors) 15114 * @private 15115 * @type {Object} 15116 */ 15117 _configs: null, 15118 /** 15119 * Returns the current value of the attribute. 15120 * @method get 15121 * @param {String} key The attribute whose value will be returned. 15122 * @return {Any} The current value of the attribute. 15123 */ 15124 get: function(key){ 15125 this._configs = this._configs || {}; 15126 var config = this._configs[key]; 15127 15128 if (!config || !this._configs.hasOwnProperty(key)) { 15129 YAHOO.log(key + ' not found', 'error', 'AttributeProvider'); 15130 return null; 15131 } 15132 15133 return config.getValue(); 15134 }, 15135 15136 /** 15137 * Sets the value of a config. 15138 * @method set 15139 * @param {String} key The name of the attribute 15140 * @param {Any} value The value to apply to the attribute 15141 * @param {Boolean} silent Whether or not to suppress change events 15142 * @return {Boolean} Whether or not the value was set. 15143 */ 15144 set: function(key, value, silent){ 15145 this._configs = this._configs || {}; 15146 var config = this._configs[key]; 15147 15148 if (!config) { 15149 YAHOO.log('set failed: ' + key + ' not found', 15150 'error', 'AttributeProvider'); 15151 return false;
15152 } 15153 15154 return config.setValue(value, silent); 15155 }, 15156 15157 /** 15158 * Returns an array of attribute names. 15159 * @method getAttributeKeys 15160 * @return {Array} An array of attribute names. 15161 */ 15162 getAttributeKeys: function(){ 15163 this._configs = this._configs; 15164 var keys = [], key; 15165 15166 for (key in this._configs) { 15167 if ( Lang.hasOwnProperty(this._configs, key) && 15168 !Lang.isUndefined(this._configs[key]) ) { 15169 keys[keys.length] = key; 15170 } 15171 } 15172 15173 return keys; 15174 }, 15175 15176 /** 15177 * Sets multiple attribute values. 15178 * @method setAttributes 15179 * @param {Object} map A key-value map of attributes 15180 * @param {Boolean} silent Whether or not to suppress change events 15181 */ 15182 setAttributes: function(map, silent){ 15183 for (var key in map) { 15184 if ( Lang.hasOwnProperty(map, key) ) { 15185 this.set(key, map[key], silent); 15186 } 15187 } 15188 }, 15189 15190 /** 15191 * Resets the specified attribute's value to its initial value. 15192 * @method resetValue 15193 * @param {String} key The name of the attribute 15194 * @param {Boolean} silent Whether or not to suppress change events 15195 * @return {Boolean} Whether or not the value was set 15196 */ 15197 resetValue: function(key, silent){ 15198 this._configs = this._configs || {}; 15199 if (this._configs[key]) { 15200 this.set(key, this._configs[key]._initialConfig.value, silent); 15201 return true; 15202 } 15203 return false; 15204 }, 15205 15206 /** 15207 * Sets the attribute's value to its current value. 15208 * @method refresh 15209 * @param {String | Array} key The attribute(s) to refresh 15210 * @param {Boolean} silent Whether or not to suppress change events 15211 */ 15212 refresh: function(key, silent) { 15213 this._configs = this._configs || {}; 15214 var configs = this._configs; 15215 15216 key = ( ( Lang.isString(key) ) ? [key] : key ) || 15217 this.getAttributeKeys(); 15218 15219 for (var i = 0, len = key.length; i < len; ++i) { 15220 if (configs.hasOwnProperty(key[i])) { 15221 this._configs[key[i]].refresh(silent); 15222 } 15223 } 15224 }, 15225 15226 /** 15227 * Adds an Attribute to the AttributeProvider instance. 15228 * @method register 15229 * @param {String} key The attribute's name 15230 * @param {Object} map A key-value map containing the 15231 * attribute's properties. 15232 * @deprecated Use setAttributeConfig 15233 */ 15234 register: function(key, map) { 15235 this.setAttributeConfig(key, map); 15236 }, 15237 15238 15239 /** 15240 * Returns the attribute's properties. 15241 * @method getAttributeConfig 15242 * @param {String} key The attribute's name 15243 * @private 15244 * @return {object} A key-value map containing all of the 15245 * attribute's properties. 15246 */ 15247 getAttributeConfig: function(key) { 15248 this._configs = this._configs || {}; 15249 var config = this._configs[key] || {}; 15250 var map = {}; // returning a copy to prevent overrides 15251 15252 for (key in config) { 15253 if ( Lang.hasOwnProperty(config, key) ) { 15254 map[key] = config[key]; 15255 } 15256 } 15257 15258 return map; 15259 }, 15260 15261 /** 15262 * Sets or updates an Attribute instance's properties. 15263 * @method setAttributeConfig 15264 * @param {String} key The attribute's name. 15265 * @param {Object} map A key-value map of attribute properties 15266 * @param {Boolean} init Whether or not this should become the intial config. 15267 */ 15268 setAttributeConfig: function(key, map, init) { 15269 this._configs = this._configs || {}; 15270 map = map || {}; 15271 if (!this._configs[key]) { 15272 map.name = key; 15273 this._configs[key] = this.createAttribute(map); 15274 } else { 15275 this._configs[key].configure(map, init); 15276 } 15277 }, 15278 15279 /** 15280 * Sets or updates an Attribute instance's properties. 15281 * @method configureAttribute 15282 * @param {String} key The attribute's name. 15283 * @param {Object} map A key-value map of attribute properties 15284 * @param {Boolean} init Whether or not this should become the intial config. 15285 * @deprecated Use setAttributeConfig 15286 */ 15287 configureAttribute: function(key, map, init) { 15288 this.setAttributeConfig(key, map, init); 15289 }, 15290 15291 /** 15292 * Resets an attribute to its intial configuration. 15293 * @method resetAttributeConfig 15294 * @param {String} key The attribute's name. 15295 * @private 15296 */ 15297 resetAttributeConfig: function(key){ 15298 this._configs = this._configs || {}; 15299 this._configs[key].resetConfig(); 15300 }, 15301 15302 // wrapper for EventProvider.subscribe 15303 // to create events on the fly 15304 subscribe: function(type, callback) { 15305 this._events = this._events || {}; 15306 15307 if ( !(type in this._events) ) { 15308 this._events[type] = this.createEvent(type); 15309 } 15310 15311 YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); 15312 }, 15313 15314 on: function() { 15315 this.subscribe.apply(this, arguments); 15316 }, 15317 15318 addListener: function() { 15319 this.subscribe.apply(this, arguments); 15320 }, 15321 15322 /** 15323 * Fires the attribute's beforeChange event. 15324 * @method fireBeforeChangeEvent 15325 * @param {String} key The attribute's name. 15326 * @param {Obj} e The event object to pass to handlers. 15327 */ 15328 fireBeforeChangeEvent: function(e) { 15329 var type = 'before'; 15330 type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change'; 15331 e.type = type; 15332 return this.fireEvent(e.type, e); 15333 }, 15334 15335 /** 15336 * Fires the attribute's change event. 15337 * @method fireChangeEvent 15338 * @param {String} key The attribute's name. 15339 * @param {Obj} e The event object to pass to the handlers. 15340 */ 15341 fireChangeEvent: function(e) { 15342 e.type += 'Change'; 15343 return this.fireEvent(e.type, e); 15344 }, 15345 15346 createAttribute: function(map) { 15347 return new YAHOO.util.Attribute(map, this); 15348 } 15349 }; 15350 15351 YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider); 15352})(); 15353 15354(function() { 15355// internal shorthand 15356var Dom = YAHOO.util.Dom, 15357 AttributeProvider = YAHOO.util.AttributeProvider, 15358 specialTypes = { 15359 mouseenter: true, 15360 mouseleave: true 15361 }; 15362 15363/** 15364 * Element provides an wrapper object to simplify adding 15365 * event listeners, using dom methods, and managing attributes. 15366 * @module element 15367 * @namespace YAHOO.util 15368 * @requires yahoo, dom, event 15369 */ 15370 15371/** 15372 * Element provides an wrapper object to simplify adding 15373 * event listeners, using dom methods, and managing attributes. 15374 * @class Element 15375 * @uses YAHOO.util.AttributeProvider 15376 * @constructor 15377 * @param el {HTMLElement | String} The html element that 15378 * represents the Element. 15379 * @param {Object} map A key-value map of initial config names and values 15380 */ 15381var Element = function(el, map) { 15382 this.init.apply(this, arguments); 15383}; 15384 15385Element.DOM_EVENTS = { 15386 'click': true, 15387 'dblclick': true, 15388 'keydown': true, 15389 'keypress': true, 15390 'keyup': true, 15391 'mousedown': true, 15392 'mousemove': true, 15393 'mouseout': true, 15394 'mouseover': true, 15395 'mouseup': true, 15396 'mouseenter': true, 15397 'mouseleave': true,
15398 'focus': true, 15399 'blur': true, 15400 'submit': true, 15401 'change': true 15402}; 15403 15404Element.prototype = { 15405 /** 15406 * Dom events supported by the Element instance. 15407 * @property DOM_EVENTS 15408 * @type Object 15409 */ 15410 DOM_EVENTS: null, 15411 15412 DEFAULT_HTML_SETTER: function(value, key) { 15413 var el = this.get('element'); 15414 15415 if (el) { 15416 el[key] = value; 15417 } 15418 15419 return value; 15420 15421 }, 15422 15423 DEFAULT_HTML_GETTER: function(key) { 15424 var el = this.get('element'), 15425 val; 15426 15427 if (el) { 15428 val = el[key]; 15429 } 15430 15431 return val; 15432 }, 15433 15434 /** 15435 * Wrapper for HTMLElement method. 15436 * @method appendChild 15437 * @param {YAHOO.util.Element || HTMLElement} child The element to append. 15438 * @return {HTMLElement} The appended DOM element. 15439 */ 15440 appendChild: function(child) { 15441 child = child.get ? child.get('element') : child; 15442 return this.get('element').appendChild(child); 15443 }, 15444 15445 /** 15446 * Wrapper for HTMLElement method. 15447 * @method getElementsByTagName 15448 * @param {String} tag The tagName to collect 15449 * @return {HTMLCollection} A collection of DOM elements. 15450 */ 15451 getElementsByTagName: function(tag) { 15452 return this.get('element').getElementsByTagName(tag); 15453 }, 15454 15455 /** 15456 * Wrapper for HTMLElement method. 15457 * @method hasChildNodes 15458 * @return {Boolean} Whether or not the element has childNodes 15459 */ 15460 hasChildNodes: function() { 15461 return this.get('element').hasChildNodes(); 15462 }, 15463 15464 /** 15465 * Wrapper for HTMLElement method. 15466 * @method insertBefore 15467 * @param {HTMLElement} element The HTMLElement to insert 15468 * @param {HTMLElement} before The HTMLElement to insert 15469 * the element before. 15470 * @return {HTMLElement} The inserted DOM element. 15471 */ 15472 insertBefore: function(element, before) { 15473 element = element.get ? element.get('element') : element; 15474 before = (before && before.get) ? before.get('element') : before; 15475 15476 return this.get('element').insertBefore(element, before); 15477 }, 15478 15479 /** 15480 * Wrapper for HTMLElement method. 15481 * @method removeChild 15482 * @param {HTMLElement} child The HTMLElement to remove 15483 * @return {HTMLElement} The removed DOM element. 15484 */ 15485 removeChild: function(child) { 15486 child = child.get ? child.get('element') : child; 15487 return this.get('element').removeChild(child); 15488 }, 15489 15490 /** 15491 * Wrapper for HTMLElement method. 15492 * @method replaceChild 15493 * @param {HTMLElement} newNode The HTMLElement to insert 15494 * @param {HTMLElement} oldNode The HTMLElement to replace 15495 * @return {HTMLElement} The replaced DOM element. 15496 */ 15497 replaceChild: function(newNode, oldNode) { 15498 newNode = newNode.get ? newNode.get('element') : newNode; 15499 oldNode = oldNode.get ? oldNode.get('element') : oldNode; 15500 return this.get('element').replaceChild(newNode, oldNode); 15501 }, 15502 15503 15504 /** 15505 * Registers Element specific attributes. 15506 * @method initAttributes 15507 * @param {Object} map A key-value map of initial attribute configs 15508 */ 15509 initAttributes: function(map) { 15510 }, 15511 15512 /** 15513 * Adds a listener for the given event. These may be DOM or 15514 * customEvent listeners. Any event that is fired via fireEvent 15515 * can be listened for. All handlers receive an event object. 15516 * @method addListener 15517 * @param {String} type The name of the event to listen for 15518 * @param {Function} fn The handler to call when the event fires 15519 * @param {Any} obj A variable to pass to the handler 15520 * @param {Object} scope The object to use for the scope of the handler 15521 */ 15522 addListener: function(type, fn, obj, scope) { 15523 15524 scope = scope || this; 15525 15526 var Event = YAHOO.util.Event, 15527 el = this.get('element') || this.get('id'), 15528 self = this; 15529 15530 15531 if (specialTypes[type] && !Event._createMouseDelegate) { 15532 YAHOO.log("Using a " + type + " event requires the event-mouseenter module", "error", "Event"); 15533 return false; 15534 } 15535 15536 15537 if (!this._events[type]) { // create on the fly 15538 15539 if (el && this.DOM_EVENTS[type]) { 15540 Event.on(el, type, function(e, matchedEl) { 15541 15542 // Supplement IE with target, currentTarget relatedTarget 15543 15544 if (e.srcElement && !e.target) { 15545 e.target = e.srcElement; 15546 } 15547 15548 if ((e.toElement && !e.relatedTarget) || (e.fromElement && !e.relatedTarget)) { 15549 e.relatedTarget = Event.getRelatedTarget(e); 15550 } 15551 15552 if (!e.currentTarget) { 15553 e.currentTarget = el; 15554 } 15555 15556 // Note: matchedEl el is passed back for delegated listeners 15557 self.fireEvent(type, e, matchedEl); 15558 15559 }, obj, scope); 15560 } 15561 this.createEvent(type, {scope: this}); 15562 } 15563 15564 return YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); // notify via customEvent 15565 }, 15566 15567 15568 /** 15569 * Alias for addListener 15570 * @method on 15571 * @param {String} type The name of the event to listen for 15572 * @param {Function} fn The function call when the event fires 15573 * @param {Any} obj A variable to pass to the handler 15574 * @param {Object} scope The object to use for the scope of the handler 15575 */ 15576 on: function() { 15577 return this.addListener.apply(this, arguments); 15578 }, 15579 15580 /** 15581 * Alias for addListener 15582 * @method subscribe 15583 * @param {String} type The name of the event to listen for 15584 * @param {Function} fn The function call when the event fires 15585 * @param {Any} obj A variable to pass to the handler 15586 * @param {Object} scope The object to use for the scope of the handler 15587 */ 15588 subscribe: function() { 15589 return this.addListener.apply(this, arguments); 15590 }, 15591 15592 /** 15593 * Remove an event listener 15594 * @method removeListener 15595 * @param {String} type The name of the event to listen for 15596 * @param {Function} fn The function call when the event fires 15597 */
15598 removeListener: function(type, fn) { 15599 return this.unsubscribe.apply(this, arguments); 15600 }, 15601 15602 /** 15603 * Wrapper for Dom method. 15604 * @method addClass 15605 * @param {String} className The className to add 15606 */ 15607 addClass: function(className) { 15608 Dom.addClass(this.get('element'), className); 15609 }, 15610 15611 /** 15612 * Wrapper for Dom method. 15613 * @method getElementsByClassName 15614 * @param {String} className The className to collect 15615 * @param {String} tag (optional) The tag to use in 15616 * conjunction with class name 15617 * @return {Array} Array of HTMLElements 15618 */ 15619 getElementsByClassName: function(className, tag) { 15620 return Dom.getElementsByClassName(className, tag, 15621 this.get('element') ); 15622 }, 15623 15624 /** 15625 * Wrapper for Dom method. 15626 * @method hasClass 15627 * @param {String} className The className to add 15628 * @return {Boolean} Whether or not the element has the class name 15629 */ 15630 hasClass: function(className) { 15631 return Dom.hasClass(this.get('element'), className); 15632 }, 15633 15634 /** 15635 * Wrapper for Dom method. 15636 * @method removeClass 15637 * @param {String} className The className to remove 15638 */ 15639 removeClass: function(className) { 15640 return Dom.removeClass(this.get('element'), className); 15641 }, 15642 15643 /** 15644 * Wrapper for Dom method. 15645 * @method replaceClass 15646 * @param {String} oldClassName The className to replace 15647 * @param {String} newClassName The className to add 15648 */ 15649 replaceClass: function(oldClassName, newClassName) { 15650 return Dom.replaceClass(this.get('element'), 15651 oldClassName, newClassName); 15652 }, 15653 15654 /** 15655 * Wrapper for Dom method. 15656 * @method setStyle 15657 * @param {String} property The style property to set 15658 * @param {String} value The value to apply to the style property 15659 */ 15660 setStyle: function(property, value) { 15661 return Dom.setStyle(this.get('element'), property, value); // TODO: always queuing? 15662 }, 15663 15664 /** 15665 * Wrapper for Dom method. 15666 * @method getStyle 15667 * @param {String} property The style property to retrieve 15668 * @return {String} The current value of the property 15669 */ 15670 getStyle: function(property) { 15671 return Dom.getStyle(this.get('element'), property); 15672 }, 15673 15674 /** 15675 * Apply any queued set calls. 15676 * @method fireQueue 15677 */ 15678 fireQueue: function() { 15679 var queue = this._queue; 15680 for (var i = 0, len = queue.length; i < len; ++i) { 15681 this[queue[i][0]].apply(this, queue[i][1]); 15682 } 15683 }, 15684 15685 /** 15686 * Appends the HTMLElement into either the supplied parentNode. 15687 * @method appendTo 15688 * @param {HTMLElement | Element} parentNode The node to append to 15689 * @param {HTMLElement | Element} before An optional node to insert before 15690 * @return {HTMLElement} The appended DOM element. 15691 */ 15692 appendTo: function(parent, before) { 15693 parent = (parent.get) ? parent.get('element') : Dom.get(parent); 15694 15695 this.fireEvent('beforeAppendTo', { 15696 type: 'beforeAppendTo', 15697 target: parent 15698 }); 15699 15700 15701 before = (before && before.get) ? 15702 before.get('element') : Dom.get(before); 15703 var element = this.get('element'); 15704 15705 if (!element) { 15706 YAHOO.log('appendTo failed: element not available', 15707 'error', 'Element'); 15708 return false; 15709 } 15710 15711 if (!parent) { 15712 YAHOO.log('appendTo failed: parent not available', 15713 'error', 'Element'); 15714 return false; 15715 } 15716 15717 if (element.parent != parent) { 15718 if (before) { 15719 parent.insertBefore(element, before); 15720 } else { 15721 parent.appendChild(element); 15722 } 15723 } 15724 15725 YAHOO.log(element + 'appended to ' + parent); 15726 15727 this.fireEvent('appendTo', { 15728 type: 'appendTo', 15729 target: parent 15730 }); 15731 15732 return element; 15733 }, 15734 15735 get: function(key) { 15736 var configs = this._configs || {}, 15737 el = configs.element; // avoid loop due to 'element' 15738 15739 if (el && !configs[key] && !YAHOO.lang.isUndefined(el.value[key]) ) { 15740 this._setHTMLAttrConfig(key); 15741 } 15742 15743 return AttributeProvider.prototype.get.call(this, key); 15744 }, 15745 15746 setAttributes: function(map, silent) { 15747 // set based on configOrder 15748 var done = {}, 15749 configOrder = this._configOrder; 15750 15751 // set based on configOrder 15752 for (var i = 0, len = configOrder.length; i < len; ++i) { 15753 if (map[configOrder[i]] !== undefined) { 15754 done[configOrder[i]] = true; 15755 this.set(configOrder[i], map[configOrder[i]], silent); 15756 } 15757 } 15758 15759 // unconfigured (e.g. Dom attributes) 15760 for (var att in map) { 15761 if (map.hasOwnProperty(att) && !done[att]) { 15762 this.set(att, map[att], silent); 15763 } 15764 } 15765 }, 15766 15767 set: function(key, value, silent) { 15768 var el = this.get('element'); 15769 if (!el) { 15770 this._queue[this._queue.length] = ['set', arguments]; 15771 if (this._configs[key]) { 15772 this._configs[key].value = value; // so "get" works while queueing 15773 15774 } 15775 return; 15776 } 15777 15778 // set it on the element if not configured and is an HTML attribute 15779 if ( !this._configs[key] && !YAHOO.lang.isUndefined(el[key]) ) { 15780 this._setHTMLAttrConfig(key); 15781 } 15782 15783 return AttributeProvider.prototype.set.apply(this, arguments); 15784 }, 15785 15786 setAttributeConfig: function(key, map, init) { 15787 this._configOrder.push(key); 15788 AttributeProvider.prototype.setAttributeConfig.apply(this, arguments); 15789 }, 15790 15791 createEvent: function(type, config) { 15792 this._events[type] = true; 15793 return AttributeProvider.prototype.createEvent.apply(this, arguments); 15794 }, 15795 15796 init: function(el, attr) { 15797 this._initElement(el, attr); 15798 }, 15799 15800 destroy: function() { 15801 var el = this.get('element'); 15802 YAHOO.util.Event.purgeElement(el, true); // purge DOM listeners recursively 15803 this.unsubscribeAll(); // unsubscribe all custom events 15804 15805 if (el && el.parentNode) { 15806 el.parentNode.removeChild(el); // pull from the DOM 15807 } 15808 15809 // revert initial configs 15810 this._queue = []; 15811 this._events = {}; 15812 this._configs = {}; 15813 this._configOrder = []; 15814 }, 15815 15816 _initElement: function(el, attr) { 15817 this._queue = this._queue || []; 15818 this._events = this._events || {}; 15819 this._configs = this._configs || {}; 15820 this._configOrder = []; 15821 attr = attr || {}; 15822 attr.element = attr.element || el || null; 15823 15824 var isReady = false; // to determine when to init HTMLElement and content 15825 15826 var DOM_EVENTS = Element.DOM_EVENTS; 15827 this.DOM_EVENTS = this.DOM_EVENTS || {}; 15828 15829 for (var event in DOM_EVENTS) { 15830 if (DOM_EVENTS.hasOwnProperty(event)) { 15831 this.DOM_EVENTS[event] = DOM_EVENTS[event]; 15832 } 15833 } 15834 15835 if (typeof attr.element === 'string') { // register ID for get() access 15836 this._setHTMLAttrConfig('id', { value: attr.element }); 15837 } 15838 15839 if (Dom.get(attr.element)) { 15840 isReady = true; 15841 this._initHTMLElement(attr); 15842 this._initContent(attr); 15843 } 15844 15845 YAHOO.util.Event.onAvailable(attr.element, function() { 15846 if (!isReady) { // otherwise already done 15847 this._initHTMLElement(attr); 15848 } 15849 15850 this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) }); 15851 }, this, true); 15852 15853 YAHOO.util.Event.onContentReady(attr.element, function() { 15854 if (!isReady) { // otherwise already done 15855 this._initContent(attr); 15856 } 15857 this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) }); 15858 }, this, true); 15859 }, 15860 15861 _initHTMLElement: function(attr) { 15862 /** 15863 * The HTMLElement the Element instance refers to. 15864 * @attribute element 15865 * @type HTMLElement 15866 */ 15867 this.setAttributeConfig('element', { 15868 value: Dom.get(attr.element), 15869 readOnly: true 15870 }); 15871 }, 15872 15873 _initContent: function(attr) { 15874 this.initAttributes(attr); 15875 this.setAttributes(attr, true); 15876 this.fireQueue(); 15877 15878 }, 15879 15880 /** 15881 * Sets the value of the property and fires beforeChange and change events. 15882 * @private 15883 * @method _setHTMLAttrConfig 15884 * @param {YAHOO.util.Element} element The Element instance to 15885 * register the config to. 15886 * @param {String} key The name of the config to register 15887 * @param {Object} map A key-value map of the config's params 15888 */ 15889 _setHTMLAttrConfig: function(key, map) { 15890 var el = this.get('element'); 15891 map = map || {}; 15892 map.name = key; 15893 15894 map.setter = map.setter || this.DEFAULT_HTML_SETTER; 15895 map.getter = map.getter || this.DEFAULT_HTML_GETTER; 15896 15897 map.value = map.value || el[key]; 15898 this._configs[key] = new YAHOO.util.Attribute(map, this); 15899 } 15900}; 15901 15902/** 15903 * Fires when the Element's HTMLElement can be retrieved by Id. 15904 * <p>See: <a href="#addListener">Element.addListener</a></p> 15905 * <p><strong>Event fields:</strong><br> 15906 * <code><
15906String> type</code> available<br> 15907 * <code><HTMLElement> 15908 * target</code> the HTMLElement bound to this Element instance<br> 15909 * <p><strong>Usage:</strong><br> 15910 * <code>var handler = function(e) {var target = e.target};<br> 15911 * myTabs.addListener('available', handler);</code></p> 15912 * @event available 15913 */ 15914 15915/** 15916 * Fires when the Element's HTMLElement subtree is rendered. 15917 * <p>See: <a href="#addListener">Element.addListener</a></p> 15918 * <p><strong>Event fields:</strong><br> 15919 * <code><String> type</code> contentReady<br> 15920 * <code><HTMLElement> 15921 * target</code> the HTMLElement bound to this Element instance<br> 15922 * <p><strong>Usage:</strong><br> 15923 * <code>var handler = function(e) {var target = e.target};<br> 15924 * myTabs.addListener('contentReady', handler);</code></p> 15925 * @event contentReady 15926 */ 15927 15928/** 15929 * Fires before the Element is appended to another Element. 15930 * <p>See: <a href="#addListener">Element.addListener</a></p> 15931 * <p><strong>Event fields:</strong><br> 15932 * <code><String> type</code> beforeAppendTo<br> 15933 * <code><HTMLElement/Element> 15934 * target</code> the HTMLElement/Element being appended to 15935 * <p><strong>Usage:</strong><br> 15936 * <code>var handler = function(e) {var target = e.target};<br> 15937 * myTabs.addListener('beforeAppendTo', handler);</code></p> 15938 * @event beforeAppendTo 15939 */ 15940 15941/** 15942 * Fires after the Element is appended to another Element. 15943 * <p>See: <a href="#addListener">Element.addListener</a></p> 15944 * <p><strong>Event fields:</strong><br> 15945 * <code><String> type</code> appendTo<br> 15946 * <code><HTMLElement/Element> 15947 * target</code> the HTMLElement/Element being appended to 15948 * <p><strong>Usage:</strong><br> 15949 * <code>var handler = function(e) {var target = e.target};<br> 15950 * myTabs.addListener('appendTo', handler);</code></p> 15951 * @event appendTo 15952 */ 15953 15954YAHOO.augment(Element, AttributeProvider); 15955YAHOO.util.Element = Element; 15956})(); 15957 15958YAHOO.register("element", YAHOO.util.Element, {version: "2.9.0", build: "2800"}); 15959/* 15960Copyright (c) 2011, Yahoo! Inc. All rights reserved. 15961Code licensed under the BSD License: 15962http://developer.yahoo.com/yui/license.html 15963version: 2.9.0 15964*/ 15965/** 15966 * Provides a mechanism to fetch remote resources and 15967 * insert them into a document 15968 * This utility can fetch JavaScript and CSS files, inserting script 15969 * tags for script and link tags for CSS. Note, this 15970 * is done via the normal browser mechanisms for inserting 15971 * these resources and making the content available to 15972 * code that would access it. Be careful when retreiving 15973 * remote resources. Only use this utility to fetch 15974 * files from sites you trust. 15975 * 15976 * @module get 15977 * @requires yahoo 15978 */ 15979 15980/** 15981 * Fetches and inserts one or more script or link nodes into the document. 15982 * This utility can fetch JavaScript and CSS files, inserting script 15983 * tags for script and link tags for CSS. Note, this 15984 * is done via the normal browser mechanisms for inserting 15985 * these resources and making the content available to 15986 * code that would access it. Be careful when retreiving 15987 * remote resources. Only use this utility to fetch 15988 * files from sites you trust. 15989 * 15990 * @namespace YAHOO.util 15991 * @class YAHOO.util.Get 15992 */ 15993YAHOO.util.Get = function() { 15994 15995 /** 15996 * hash of queues to manage multiple requests 15997 * @property queues 15998 * @private 15999 */ 16000 var queues={}, 16001 16002 /** 16003 * queue index used to generate transaction ids 16004 * @property qidx 16005 * @type int 16006 * @private 16007 */ 16008 qidx=0, 16009 16010 /** 16011 * node index used to generate unique node ids 16012 * @property nidx 16013 * @type int 16014 * @private 16015 */ 16016 nidx=0, 16017 16018 /** 16019 * interal property used to prevent multiple simultaneous purge 16020 * processes 16021 * @property purging 16022 * @type boolean 16023 * @private 16024 */ 16025 _purging=false, 16026 16027 ua=YAHOO.env.ua, 16028 16029 lang=YAHOO.lang, 16030 16031 _fail, 16032 _purge, 16033 _track, 16034 16035 /** 16036 * Generates an HTML element, this is not appended to a document 16037 * @method _node 16038 * @param type {string} the type of element 16039 * @param attr {string} the attributes 16040 * @param win {Window} optional window to create the element in 16041 * @return {HTMLElement} the generated node 16042 * @private 16043 */ 16044 _node = function(type, attr, win) { 16045 var w = win || window, d=w.document, n=d.createElement(type), i; 16046 16047 for (i in attr) { 16048 if (attr.hasOwnProperty(i)) { 16049 n.setAttribute(i, attr[i]); 16050 } 16051 } 16052 16053 return n; 16054 }, 16055 16056 /** 16057 * Generates a link node 16058 * @method _linkNode 16059 * @param url {string} the url for the css file 16060 * @param win {Window} optional window to create the node in 16061 * @return {HTMLElement} the generated node 16062 * @private 16063 */ 16064 _linkNode = function(url, win, attributes) { 16065 16066 var o = { 16067 id: "yui__dyn_" + (nidx++), 16068 type: "text/css", 16069 rel: "stylesheet", 16070 href: url 16071 }; 16072 16073 if (attributes) { 16074 lang.augmentObject(o, attributes); 16075 } 16076 16077 return _node("link", o, win); 16078 }, 16079 16080 /** 16081 * Generates a script node 16082 * @method _scriptNode 16083 * @param url {string} the url for the script file 16084 * @param win {Window} optional window to create the node in 16085 * @return {HTMLElement} the generated node 16086 * @private 16087 */ 16088 _scriptNode = function(url, win, attributes) { 16089 var o = { 16090 id: "yui__dyn_" + (nidx++), 16091 type: "text/javascript", 16092 src: url 16093 }; 16094 16095 if (attributes) { 16096 lang.augmentObject(o, attributes); 16097 } 16098 16099 return _node("script", o, win); 16100 }, 16101 16102 /** 16103 * Returns the data payload for callback functions 16104 * @method _returnData 16105 * @private 16106 */ 16107 _returnData = function(q, msg) { 16108 return { 16109 tId: q.tId, 16110 win: q.win, 16111 data: q.data, 16112 nodes: q.nodes, 16113 msg: msg, 16114 purge: function() { 16115 _purge(this.tId); 16116 } 16117 }; 16118 }, 16119 16120 _get = function(nId, tId) { 16121 var q = queues[tId], 16122 n = (lang.isString(nId)) ? q.win.document.getElementById(nId) : nId; 16123 if (!n) { 16124 _fail(tId, "target node not found: " + nId); 16125 } 16126 16127 return n; 16128 }, 16129 16130 16131 /** 16132 * The request is complete, so executing the requester's callback 16133 * @method _finish 16134 * @param id {string} the id of the request 16135 * @private 16136 */ 16137 _finish = function(id) { 16138 YAHOO.log("Finishing transaction " + id); 16139 var q = queues[id], msg, context; 16140 q.finished = true; 16141 16142 if (q.aborted) { 16143 msg = "transaction " + id + " was aborted"; 16144 _fail(id, msg); 16145 return; 16146 } 16147 16148 // execute success callback 16149 if (q.onSuccess) { 16150 context = q.scope || q.win; 16151 q.onSuccess.call(context, _returnData(q)); 16152 } 16153 }, 16154 16155 /** 16156 * Timeout detected 16157 * @method _timeout 16158 * @param id {string} the id of the request 16159 * @private 16160 */ 16161 _timeout = function(id) { 16162 YAHOO.log("Timeout " + id, "info", "get"); 16163 var q = queues[id], context; 16164 if (q.onTimeout) { 16165 context = q.scope || q; 16166 q.onTimeout.call(context, _returnData(q)); 16167 } 16168 }, 16169 16170 /** 16171 * Loads the next item for a given request 16172 * @method _next 16173 * @param id {string} the id of the request 16174 * @param loaded {string} the url that was just loaded, if any 16175 * @private 16176 */ 16177 _next = function(id, loaded) { 16178 16179 YAHOO.log("_next: " + id + ", loaded: " + loaded, "info", "Get"); 16180 16181 var q = queues[id], w=q.win, d=w.document, h=d.getElementsByTagName("head")[0], 16182 n, msg, url, s, extra; 16183 16184 if (q.timer) { 16185 // Y.log('cancel timer'); 16186 q.timer.cancel(); 16187 } 16188 16189 if (q.aborted) { 16190 msg = "transaction " + id + " was aborted"; 16191 _fail(id, msg); 16192 return; 16193 } 16194 16195 if (loaded) {
16196 q.url.shift(); 16197 if (q.varName) { 16198 q.varName.shift(); 16199 } 16200 } else { 16201 // This is the first pass: make sure the url is an array 16202 q.url = (lang.isString(q.url)) ? [q.url] : q.url; 16203 if (q.varName) { 16204 q.varName = (lang.isString(q.varName)) ? [q.varName] : q.varName; 16205 } 16206 } 16207 16208 16209 if (q.url.length === 0) { 16210 // Safari 2.x workaround - There is no way to know when 16211 // a script is ready in versions of Safari prior to 3.x. 16212 // Adding an extra node reduces the problem, but doesn't 16213 // eliminate it completely because the browser executes 16214 // them asynchronously. 16215 if (q.type === "script" && ua.webkit && ua.webkit < 420 && 16216 !q.finalpass && !q.varName) { 16217 // Add another script node. This does not guarantee that the 16218 // scripts will execute in order, but it does appear to fix the 16219 // problem on fast connections more effectively than using an 16220 // arbitrary timeout. It is possible that the browser does 16221 // block subsequent script execution in this case for a limited 16222 // time. 16223 extra = _scriptNode(null, q.win, q.attributes); 16224 extra.innerHTML='YAHOO.util.Get._finalize("' + id + '");'; 16225 q.nodes.push(extra); h.appendChild(extra); 16226 16227 } else { 16228 _finish(id); 16229 } 16230 16231 return; 16232 } 16233 16234 16235 url = q.url[0]; 16236 16237 // if the url is undefined, this is probably a trailing comma problem in IE 16238 if (!url) {
16239 q.url.shift(); 16240 YAHOO.log('skipping empty url'); 16241 return _next(id); 16242 } 16243 16244 YAHOO.log("attempting to load " + url, "info", "Get"); 16245 16246 if (q.timeout) { 16247 // Y.log('create timer'); 16248 q.timer = lang.later(q.timeout, q, _timeout, id); 16249 } 16250 16251 if (q.type === "script") { 16252 n = _scriptNode(url, w, q.attributes); 16253 } else { 16254 n = _linkNode(url, w, q.attributes); 16255 } 16256 16257 // track this node's load progress 16258 _track(q.type, n, id, url, w, q.url.length); 16259 16260 // add the node to the queue so we can return it to the user supplied callback 16261 q.nodes.push(n); 16262 16263 // add it to the head or insert it before 'insertBefore' 16264 if (q.insertBefore) { 16265 s = _get(q.insertBefore, id); 16266 if (s) { 16267 s.parentNode.insertBefore(n, s); 16268 } 16269 } else { 16270 h.appendChild(n); 16271 } 16272 16273 YAHOO.log("Appending node: " + url, "info", "Get"); 16274 16275 // FireFox does not support the onload event for link nodes, so there is 16276 // no way to make the css requests synchronous. This means that the css 16277 // rules in multiple files could be applied out of order in this browser 16278 // if a later request returns before an earlier one. Safari too. 16279 if ((ua.webkit || ua.gecko) && q.type === "css") { 16280 _next(id, url); 16281 } 16282 }, 16283 16284 /** 16285 * Removes processed queues and corresponding nodes 16286 * @method _autoPurge 16287 * @private 16288 */ 16289 _autoPurge = function() { 16290 16291 if (_purging) { 16292 return; 16293 } 16294 16295 _purging = true; 16296 16297 var i, q; 16298 16299 for (i in queues) { 16300 if (queues.hasOwnProperty(i)) { 16301 q = queues[i]; 16302 if (q.autopurge && q.finished) { 16303 _purge(q.tId); 16304 delete queues[i]; 16305 } 16306 } 16307 } 16308 16309 _purging = false; 16310 }, 16311 16312 /** 16313 * Saves the state for the request and begins loading 16314 * the requested urls 16315 * @method queue 16316 * @param type {string} the type of node to insert 16317 * @param url {string} the url to load 16318 * @param opts the hash of options for this request 16319 * @private 16320 */ 16321 _queue = function(type, url, opts) { 16322 16323 var id = "q" + (qidx++), q; 16324 opts = opts || {}; 16325 16326 if (qidx % YAHOO.util.Get.PURGE_THRESH === 0) { 16327 _autoPurge(); 16328 } 16329 16330 queues[id] = lang.merge(opts, { 16331 tId: id, 16332 type: type, 16333 url: url, 16334 finished: false, 16335 aborted: false, 16336 nodes: [] 16337 }); 16338 16339 q = queues[id]; 16340 q.win = q.win || window; 16341 q.scope = q.scope || q.win; 16342 q.autopurge = ("autopurge" in q) ? q.autopurge : 16343 (type === "script") ? true : false; 16344 16345 q.attributes = q.attributes || {}; 16346 q.attributes.charset = opts.charset || q.attributes.charset || 'utf-8'; 16347 16348 lang.later(0, q, _next, id); 16349 16350 return { 16351 tId: id 16352 }; 16353 }; 16354 16355 /** 16356 * Detects when a node has been loaded. In the case of 16357 * script nodes, this does not guarantee that contained 16358 * script is ready to use. 16359 * @method _track 16360 * @param type {string} the type of node to track 16361 * @param n {HTMLElement} the node to track 16362 * @param id {string} the id of the request 16363 * @param url {string} the url that is being loaded 16364 * @param win {Window} the targeted window 16365 * @param qlength the number of remaining items in the queue, 16366 * including this one 16367 * @param trackfn {Function} function to execute when finished 16368 * the default is _next 16369 * @private 16370 */ 16371 _track = function(type, n, id, url, win, qlength, trackfn) { 16372 var f = trackfn || _next, rs, q, a, freq, w, l, i, msg; 16373 16374 // IE supports the readystatechange event for script and css nodes 16375 if (ua.ie && ua.ie < 11) { 16376 n.onreadystatechange = function() { 16377 rs = this.readyState; 16378 if ("loaded" === rs || "complete" === rs) { 16379 YAHOO.log(id + " onload " + url, "info", "Get"); 16380 n.onreadystatechange = null; 16381 f(id, url); 16382 } 16383 }; 16384 16385 // webkit prior to 3.x is problemmatic 16386 } else if (ua.webkit) { 16387 16388 if (type === "script") { 16389
16390 // Safari 3.x supports the load event for script nodes (DOM2) 16391 if (ua.webkit >= 420) { 16392 16393 n.addEventListener("load", function() { 16394 YAHOO.log(id + " DOM2 onload " + url, "info", "Get"); 16395 f(id, url); 16396 }); 16397 16398 // Nothing can be done with Safari < 3.x except to pause and hope 16399 // for the best, particularly after last script is inserted. The 16400 // scripts will always execute in the order they arrive, not 16401 // necessarily the order in which they were inserted. To support 16402 // script nodes with complete reliability in these browsers, script 16403 // nodes either need to invoke a function in the window once they 16404 // are loaded or the implementer needs to provide a well-known 16405 // property that the utility can poll for. 16406 } else { 16407 // Poll for the existence of the named variable, if it 16408 // was supplied. 16409 q = queues[id]; 16410 if (q.varName) { 16411 freq = YAHOO.util.Get.POLL_FREQ; 16412 YAHOO.log("Polling for " + q.varName[0]); 16413 q.maxattempts = YAHOO.util.Get.TIMEOUT/freq; 16414 q.attempts = 0; 16415 q._cache = q.varName[0].split("."); 16416 q.timer = lang.later(freq, q, function(o) { 16417 a = this._cache; 16418 l = a.length; 16419 w = this.win; 16420 for (i=0; i<l; i=i+1) { 16421 w = w[a[i]]; 16422 if (!w) { 16423 // if we have exausted our attempts, give up 16424 this.attempts++; 16425 if (this.attempts++ > this.maxattempts) { 16426 msg = "Over retry limit, giving up"; 16427 q.timer.cancel(); 16428 _fail(id, msg); 16429 } else { 16430 YAHOO.log(a[i] + " failed, retrying"); 16431 } 16432 return; 16433 } 16434 } 16435 16436 YAHOO.log("Safari poll complete"); 16437 16438 q.timer.cancel(); 16439 f(id, url); 16440 16441 }, null, true); 16442 } else { 16443 lang.later(YAHOO.util.Get.POLL_FREQ, null, f, [id, url]); 16444 } 16445 } 16446 } 16447
16448 // FireFox and Opera support onload (but not DOM2 in FF) handlers for 16449 // script nodes. Opera, but not FF, supports the onload event for link 16450 // nodes. 16451 } else { 16452 n.onload = function() { 16453 YAHOO.log(id + " onload " + url, "info", "Get"); 16454 f(id, url); 16455 }; 16456 } 16457 }; 16458 16459 /* 16460 * The request failed, execute fail handler with whatever 16461 * was accomplished. There isn't a failure case at the 16462 * moment unless you count aborted transactions 16463 * @method _fail 16464 * @param id {string} the id of the request 16465 * @private 16466 */ 16467 _fail = function(id, msg) { 16468 YAHOO.log("get failure: " + msg, "warn", "Get"); 16469 var q = queues[id], context; 16470 // execute failure callback 16471 if (q.onFailure) { 16472 context = q.scope || q.win; 16473 q.onFailure.call(context, _returnData(q, msg)); 16474 } 16475 }; 16476 16477 /** 16478 * Removes the nodes for the specified queue 16479 * @method _purge 16480 * @private 16481 */ 16482 _purge = function(tId) { 16483 if (queues[tId]) { 16484 16485 var q = queues[tId], 16486 nodes = q.nodes, 16487 l = nodes.length, 16488 d = q.win.document, 16489 h = d.getElementsByTagName("head")[0], 16490 sib, i, node, attr; 16491 16492 if (q.insertBefore) { 16493 sib = _get(q.insertBefore, tId); 16494 if (sib) { 16495 h = sib.parentNode; 16496 } 16497 } 16498 16499 for (i=0; i<l; i=i+1) { 16500 node = nodes[i]; 16501 if (node.clearAttributes) { 16502 node.clearAttributes(); 16503 } else { 16504 for (attr in node) { 16505 if (node.hasOwnProperty(attr)) { 16506 delete node[attr]; 16507 } 16508 } 16509 } 16510 16511 h.removeChild(node); 16512 } 16513 16514 q.nodes = []; 16515 } 16516 }; 16517 16518 16519 return { 16520 16521 /** 16522 * The default poll freqency in ms, when needed 16523 * @property POLL_FREQ 16524 * @static 16525 * @type int 16526 * @default 10 16527 */ 16528 POLL_FREQ: 10, 16529 16530 /** 16531 * The number of request required before an automatic purge. 16532 * property PURGE_THRESH 16533 * @static 16534 * @type int 16535 * @default 20 16536 */ 16537 PURGE_THRESH: 20, 16538 16539 /** 16540 * The length time to poll for varName when loading a script in 16541 * Safari 2.x before the transaction fails. 16542 * property TIMEOUT 16543 * @static 16544 * @type int 16545 * @default 2000 16546 */ 16547 TIMEOUT: 2000, 16548 16549 /** 16550 * Called by the the helper for detecting script load in Safari 16551 * @method _finalize 16552 * @param id {string} the transaction id 16553 * @private 16554 */ 16555 _finalize: function(id) { 16556 YAHOO.log(id + " finalized ", "info", "Get"); 16557 lang.later(0, null, _finish, id); 16558 }, 16559 16560 /** 16561 * Abort a transaction 16562 * @method abort 16563 * @param {string|object} either the tId or the object returned from 16564 * script() or css() 16565 */ 16566 abort: function(o) { 16567 var id = (lang.isString(o)) ? o : o.tId, 16568 q = queues[id]; 16569 if (q) { 16570 YAHOO.log("Aborting " + id, "info", "Get"); 16571 q.aborted = true; 16572 } 16573 }, 16574 16575 /** 16576 * Fetches and inserts one or more script nodes into the head 16577 * of the current document or the document in a specified window. 16578 * 16579 * @method script 16580 * @static 16581 * @param url {string|string[]} the url or urls to the script(s) 16582 * @param opts {object} Options: 16583 * <dl> 16584 * <dt>onSuccess</dt> 16585 * <dd> 16586 * callback to execute when the script(s) are finished loading 16587 * The callback receives an object back with the following 16588 * data: 16589 * <dl> 16590 * <dt>win</dt> 16591 * <dd>the window the script(s) were inserted into</dd> 16592 * <dt>data</dt> 16593 * <dd>the data object passed in when the request was made</dd> 16594 * <dt>nodes</dt> 16595 * <dd>An array containing references to the nodes that were 16596 * inserted</dd> 16597 * <dt>purge</dt> 16598 * <dd>A function that, when executed, will remove the nodes 16599 * that were inserted</dd> 16600 * <dt> 16601 * </dl> 16602 * </dd> 16603 * <dt>onFailure</dt> 16604 * <dd> 16605 * callback to execute when the script load operation fails 16606 * The callback receives an object back with the following 16607 * data: 16608 * <dl> 16609 * <dt>win</dt> 16610 * <dd>the window the script(s) were inserted into</dd> 16611 * <dt>data</dt> 16612 * <dd>the data object passed in when the request was made</dd> 16613 * <dt>nodes</dt> 16614 * <dd>An array containing references to the nodes that were 16615 * inserted successfully</dd> 16616 * <dt>purge</dt> 16617 * <dd>A function that, when executed, will remove any nodes 16618 * that were inserted</dd> 16619 * <dt> 16620 * </dl> 16621 * </dd> 16622 * <dt>onTimeout</dt> 16623 * <dd> 16624 * callback to execute when a timeout occurs. 16625 * The callback receives an object back with the following 16626 * data: 16627 * <dl> 16628 * <dt>win</dt> 16629 * <dd>the window the script(s) were inserted into</dd> 16630 * <dt>data</dt> 16631 * <dd>the data object passed in when the request was made</dd> 16632 * <dt>nodes</dt> 16633 * <dd>An array containing references to the nodes that were 16634 * inserted</dd> 16635 * <dt>purge</dt> 16636 * <dd>A function that, when executed, will remove the nodes 16637 * that were inserted</dd> 16638 * <dt> 16639 * </dl> 16640 * </dd> 16641 * <dt>scope</dt> 16642 * <dd>the execution context for the callbacks</dd> 16643 * <dt>win</dt> 16644 * <dd>a window other than the one the utility occupies</dd> 16645 * <dt>autopurge</dt> 16646 * <dd> 16647 * setting to true will let the utilities cleanup routine purge 16648 * the script once loaded 16649 * </dd> 16650 * <dt>data</dt> 16651 * <dd> 16652 * data that is supplied to the callback when the script(s) are 16653 * loaded. 16654 * </dd> 16655 * <dt>varName</dt> 16656 * <dd> 16657 * variable that should be available when a script is finished 16658 * loading. Used to help Safari 2.x and below with script load 16659 * detection. The type of this property should match what was 16660 * passed into the url parameter: if loading a single url, a 16661 * string can be supplied. If loading multiple scripts, you 16662 * must supply an array that contains the variable name for 16663 * each script. 16664 * </dd> 16665 * <dt>insertBefore</dt> 16666 * <dd>node or node id that will become the new node's nextSibling</dd> 16667 * </dl> 16668 * <dt>charset</dt> 16669 * <dd>Node charset, deprecated, use 'attributes'</dd> 16670 * <dt>attributes</dt> 16671 * <dd>A hash of attributes to apply to dynamic nodes.</dd> 16672 * <dt>timeout</dt> 16673 * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd> 16674 * <pre> 16675 * // assumes yahoo, dom, and event are already on the page 16676 * YAHOO.util.Get.script( 16677 * ["http://yui.yahooapis.com/2.7.0/build/dragdrop/dragdrop-min.js", 16678 * "http://yui.yahooapis.com/2.7.0/build/animation/animation-min.js"], { 16679 * onSuccess: function(o) { 16680 * YAHOO.log(o.data); // foo 16681 * new YAHOO.util.DDProxy("dd1"); // also new o.reference("dd1"); would work 16682 * this.log("won't cause error because YAHOO is the scope"); 16683 * this.log(o.nodes.length === 2) // true 16684 * // o.purge(); // optionally remove the script nodes immediately 16685 * }, 16686 * onFailure: function(o) { 16687 * YAHOO.log("transaction failed"); 16688 * }, 16689 * data: "foo", 16690 * timeout: 10000, // 10 second timeout 16691 * scope: YAHOO, 16692 * // win: otherframe // target another window/frame 16693 * autopurge: true // allow the utility to choose when to remove the nodes 16694 * }); 16695 * </pre> 16696 * @return {tId: string}
16696 an object containing info about the transaction 16697 */ 16698 script: function(url, opts) { return _queue("script", url, opts); }, 16699 16700 /** 16701 * Fetches and inserts one or more css link nodes into the 16702 * head of the current document or the document in a specified 16703 * window. 16704 * @method css 16705 * @static 16706 * @param url {string} the url or urls to the css file(s) 16707 * @param opts Options: 16708 * <dl> 16709 * <dt>onSuccess</dt> 16710 * <dd> 16711 * callback to execute when the css file(s) are finished loading 16712 * The callback receives an object back with the following 16713 * data: 16714 * <dl>win</dl> 16715 * <dd>the window the link nodes(s) were inserted into</dd> 16716 * <dt>data</dt> 16717 * <dd>the data object passed in when the request was made</dd> 16718 * <dt>nodes</dt> 16719 * <dd>An array containing references to the nodes that were 16720 * inserted</dd> 16721 * <dt>purge</dt> 16722 * <dd>A function that, when executed, will remove the nodes 16723 * that were inserted</dd> 16724 * <dt> 16725 * </dl> 16726 * </dd> 16727 * <dt>scope</dt> 16728 * <dd>the execution context for the callbacks</dd> 16729 * <dt>win</dt> 16730 * <dd>a window other than the one the utility occupies</dd> 16731 * <dt>data</dt> 16732 * <dd> 16733 * data that is supplied to the callbacks when the nodes(s) are 16734 * loaded. 16735 * </dd> 16736 * <dt>insertBefore</dt> 16737 * <dd>node or node id that will become the new node's nextSibling</dd> 16738 * <dt>charset</dt> 16739 * <dd>Node charset, deprecated, use 'attributes'</dd> 16740 * <dt>attributes</dt> 16741 * <dd>A hash of attributes to apply to dynamic nodes.</dd> 16742 * </dl> 16743 * <pre> 16744 * YAHOO.util.Get.css("http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css"); 16745 * </pre> 16746 * <pre> 16747 * YAHOO.util.Get.css(["http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css", 16748 * "http://yui.yahooapis.com/2.7.0/build/logger/assets/skins/sam/logger.css"]); 16749 * </pre> 16750 * @return {tId: string}
16750 an object containing info about the transaction 16751 */ 16752 css: function(url, opts) { 16753 return _queue("css", url, opts); 16754 } 16755 }; 16756}(); 16757 16758YAHOO.register("get", YAHOO.util.Get, {version: "2.9.0", build: "2800"}); 16759/* 16760Copyright (c) 2011, Yahoo! Inc. All rights reserved. 16761Code licensed under the BSD License: 16762http://developer.yahoo.com/yui/license.html 16763version: 2.9.0 16764*/ 16765/** 16766 * The YAHOO object is the single global object used by YUI Library. It 16767 * contains utility function for setting up namespaces, inheritance, and 16768 * logging. YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces 16769 * created automatically for and used by the library. 16770 * @module yahoo 16771 * @title YAHOO Global 16772 */ 16773 16774/** 16775 * YAHOO_config is not included as part of the library. Instead it is an 16776 * object that can be defined by the implementer immediately before 16777 * including the YUI library. The properties included in this object 16778 * will be used to configure global properties needed as soon as the 16779 * library begins to load. 16780 * @class YAHOO_config 16781 * @static 16782 */ 16783 16784/** 16785 * A reference to a function that will be executed every time a YAHOO module 16786 * is loaded. As parameter, this function will receive the version 16787 * information for the module. See <a href="YAHOO.env.html#getVersion"> 16788 * YAHOO.env.getVersion</a> for the description of the version data structure. 16789 * @property listener 16790 * @type Function 16791 * @static 16792 * @default undefined 16793 */ 16794 16795/** 16796 * Set to true if the library will be dynamically loaded after window.onload. 16797 * Defaults to false 16798 * @property injecting 16799 * @type boolean 16800 * @static 16801 * @default undefined 16802 */ 16803 16804/** 16805 * Instructs the yuiloader component to dynamically load yui components and 16806 * their dependencies. See the yuiloader documentation for more information 16807 * about dynamic loading 16808 * @property load 16809 * @static 16810 * @default undefined 16811 * @see yuiloader 16812 */ 16813 16814/** 16815 * Forces the use of the supplied locale where applicable in the library 16816 * @property locale 16817 * @type string 16818 * @static 16819 * @default undefined 16820 */ 16821 16822if (typeof YAHOO == "undefined" || !YAHOO) { 16823 /** 16824 * The YAHOO global namespace object. If YAHOO is already defined, the 16825 * existing YAHOO object will not be overwritten so that defined 16826 * namespaces are preserved. 16827 * @class YAHOO 16828 * @static 16829 */ 16830 var YAHOO = {}; 16831} 16832 16833/** 16834 * Returns the namespace specified and creates it if it doesn't exist 16835 * <pre> 16836 * YAHOO.namespace("property.package"); 16837 * YAHOO.namespace("YAHOO.property.package"); 16838 * </pre> 16839 * Either of the above would create YAHOO.property, then 16840 * YAHOO.property.package 16841 * 16842 * Be careful when naming packages. Reserved words may work in some browsers 16843 * and not others. For instance, the following will fail in Safari: 16844 * <pre> 16845 * YAHOO.namespace("really.long.nested.namespace"); 16846 * </pre> 16847 * This fails because "long" is a future reserved word in ECMAScript 16848 * 16849 * For implementation code that uses YUI, do not create your components 16850 * in the namespaces defined by YUI ( 16851 * <code>YAHOO.util</code>, 16852 * <code>YAHOO.widget</code>, 16853 * <code>YAHOO.lang</code>, 16854 * <code>YAHOO.tool</code>, 16855 * <code>YAHOO.example</code>, 16856 * <code>YAHOO.env</code>) -- create your own namespace (e.g., 'companyname'). 16857 * 16858 * @method namespace 16859 * @static 16860 * @param {String*} arguments 1-n namespaces to create 16861 * @return {Object} A reference to the last namespace object created 16862 */ 16863YAHOO.namespace = function() { 16864 var a=arguments, o=null, i, j, d; 16865 for (i=0; i<a.length; i=i+1) { 16866 d=(""+a[i]).split("."); 16867 o=YAHOO; 16868 16869 // YAHOO is implied, so it is ignored if it is included 16870 for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) { 16871 o[d[j]]=o[d[j]] || {}; 16872 o=o[d[j]]; 16873 } 16874 } 16875 16876 return o; 16877}; 16878 16879/** 16880 * Uses YAHOO.widget.Logger to output a log message, if the widget is 16881 * available. 16882 * Note: LogReader adds the message, category, and source to the DOM as HTML. 16883 * 16884 * @method log 16885 * @static 16886 * @param {HTML} msg The message to log. 16887 * @param {HTML} cat The log category for the message. Default 16888 * categories are "info", "warn", "error", time". 16889 * Custom categories can be used as well. (opt) 16890 * @param {HTML} src The source of the the message (opt) 16891 * @return {Boolean} True if the log operation was successful. 16892 */ 16893YAHOO.log = function(msg, cat, src) { 16894 var l=YAHOO.widget.Logger; 16895 if(l && l.log) { 16896 return l.log(msg, cat, src); 16897 } else { 16898 return false;
16899 } 16900}; 16901 16902/** 16903 * Registers a module with the YAHOO object 16904 * @method register 16905 * @static 16906 * @param {String} name the name of the module (event, slider, etc) 16907 * @param {Function} mainClass a reference to class in the module. This 16908 * class will be tagged with the version info 16909 * so that it will be possible to identify the 16910 * version that is in use when multiple versions 16911 * have loaded 16912 * @param {Object} data metadata object for the module. Currently it 16913 * is expected to contain a "version" property 16914 * and a "build" property at minimum. 16915 */ 16916YAHOO.register = function(name, mainClass, data) { 16917 var mods = YAHOO.env.modules, m, v, b, ls, i; 16918 16919 if (!mods[name]) { 16920 mods[name] = { 16921 versions:[], 16922 builds:[] 16923 }; 16924 } 16925 16926 m = mods[name]; 16927 v = data.version; 16928 b = data.build; 16929 ls = YAHOO.env.listeners; 16930 16931 m.name = name; 16932 m.version = v; 16933 m.build = b; 16934 m.versions.push(v); 16935 m.builds.push(b); 16936 m.mainClass = mainClass; 16937 16938 // fire the module load listeners 16939 for (i=0;i<ls.length;i=i+1) { 16940 ls[i](m); 16941 } 16942 // label the main class 16943 if (mainClass) { 16944 mainClass.VERSION = v; 16945 mainClass.BUILD = b; 16946 } else { 16947 YAHOO.log("mainClass is undefined for module " + name, "warn"); 16948 } 16949}; 16950 16951/** 16952 * YAHOO.env is used to keep track of what is known about the YUI library and 16953 * the browsing environment 16954 * @class YAHOO.env 16955 * @static 16956 */ 16957YAHOO.env = YAHOO.env || { 16958 16959 /** 16960 * Keeps the version info for all YUI modules that have reported themselves 16961 * @property modules 16962 * @type Object[] 16963 */ 16964 modules: [], 16965 16966 /** 16967 * List of functions that should be executed every time a YUI module 16968 * reports itself. 16969 * @property listeners 16970 * @type Function[] 16971 */ 16972 listeners: [] 16973}; 16974 16975/** 16976 * Returns the version data for the specified module: 16977 * <dl> 16978 * <dt>name:</dt> <dd>The name of the module</dd> 16979 * <dt>version:</dt> <dd>The version in use</dd> 16980 * <dt>build:</dt> <dd>The build number in use</dd> 16981 * <dt>versions:</dt> <dd>All versions that were registered</dd> 16982 * <dt>builds:</dt> <dd>All builds that were registered.</dd> 16983 * <dt>mainClass:</dt> <dd>An object that was was stamped with the 16984 * current version and build. If 16985 * mainClass.VERSION != version or mainClass.BUILD != build, 16986 * multiple versions of pieces of the library have been 16987 * loaded, potentially causing issues.</dd> 16988 * </dl> 16989 * 16990 * @method getVersion 16991 * @static 16992 * @param {String} name the name of the module (event, slider, etc) 16993 * @return {Object} The version info 16994 */ 16995YAHOO.env.getVersion = function(name) { 16996 return YAHOO.env.modules[name] || null; 16997}; 16998 16999/** 17000 * Do not fork for a browser if it can be avoided. Use feature detection when 17001 * you can. Use the user agent as a last resort. YAHOO.env.ua stores a version 17002 * number for the browser engine, 0 otherwise. This value may or may not map 17003 * to the version number of the browser using the engine. The value is 17004 * presented as a float so that it can easily be used for boolean evaluation 17005 * as well as for looking for a particular range of versions. Because of this, 17006 * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9 17007 * reports 1.8). 17008 * @class YAHOO.env.ua 17009 * @static 17010 */ 17011 17012/** 17013 * parses a user agent string (or looks for one in navigator to parse if 17014 * not supplied). 17015 * @method parseUA 17016 * @since 2.9.0 17017 * @static 17018 */ 17019YAHOO.env.parseUA = function(agent) { 17020 17021 var numberify = function(s) { 17022 var c = 0; 17023 return parseFloat(s.replace(/\./g, function() { 17024 return (c++ == 1) ? '' : '.'; 17025 })); 17026 }, 17027 17028 nav = navigator, 17029 17030 o = { 17031 17032 /** 17033 * Internet Explorer version number or 0. Example: 6 17034 * @property ie 17035 * @type float 17036 * @static 17037 */ 17038 ie: 0, 17039 17040 /** 17041 * Opera version number or 0. Example: 9.2 17042 * @property opera 17043 * @type float 17044 * @static 17045 */ 17046 opera: 0, 17047 17048 /** 17049 * Gecko engine revision number. Will evaluate to 1 if Gecko 17050 * is detected but the revision could not be found. Other browsers 17051 * will be 0. Example: 1.8 17052 * <pre> 17053 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 17054 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8 17055 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81 17056 * Firefox 3.0 <-- 1.9 17057 * Firefox 3.5 <-- 1.91 17058 * </pre> 17059 * @property gecko 17060 * @type float 17061 * @static 17062 */ 17063 gecko: 0, 17064 17065 /** 17066 * AppleWebKit version. KHTML browsers that are not WebKit browsers 17067 * will evaluate to 1, other browsers 0. Example: 418.9 17068 * <pre> 17069 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 17070 * latest available for Mac OSX 10.3. 17071 * Safari 2.0.2: 416 <-- hasOwnProperty introduced 17072 * Safari 2.0.4: 418 <-- preventDefault fixed 17073 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run 17074 * different versions of webkit 17075 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been 17076 * updated, but not updated 17077 * to the latest patch. 17078 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native 17079 * SVG and many major issues fixed). 17080 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic 17081 * update from 2.x via the 10.4.11 OS patch. 17082 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event. 17083 * yahoo.com user agent hack removed. 17084 * </pre> 17085 * http://en.wikipedia.org/wiki/Safari_version_history 17086 * @property webkit 17087 * @type float 17088 * @static 17089 */ 17090 webkit: 0, 17091 17092 /** 17093 * Chrome will be detected as webkit, but this property will also 17094 * be populated with the Chrome version number 17095 * @property chrome 17096 * @type float 17097 * @static 17098 */ 17099 chrome: 0, 17100 17101 /** 17102 * The mobile property will be set to a string containing any relevant 17103 * user agent information when a modern mobile browser is detected. 17104 * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series 17105 * devices with the WebKit-based browser, and Opera Mini. 17106 * @property mobile 17107 * @type string 17108 * @static 17109 */ 17110 mobile: null, 17111 17112 /** 17113 * Adobe AIR version number or 0. Only populated if webkit is detected. 17114 * Example: 1.0 17115 * @property air 17116 * @type float 17117 */ 17118 air: 0, 17119 /** 17120 * Detects Apple iPad's OS version 17121 * @property ipad 17122 * @type float 17123 * @static 17124 */ 17125 ipad: 0, 17126 /** 17127 * Detects Apple iPhone's OS version 17128 * @property iphone 17129 * @type float 17130 * @static 17131 */ 17132 iphone: 0, 17133 /** 17134 * Detects Apples iPod's OS version 17135 * @property ipod 17136 * @type float 17137 * @static 17138 */ 17139 ipod: 0, 17140 /** 17141 * General truthy check for iPad, iPhone or iPod 17142 * @property ios 17143 * @type float 17144 * @static 17145 */ 17146 ios: null, 17147 /** 17148 * Detects Googles Android OS version 17149 * @property android 17150 * @type float 17151 * @static 17152 */ 17153 android: 0, 17154 /** 17155 * Detects Palms WebOS version 17156 * @property webos 17157 * @type float 17158 * @static 17159 */ 17160 webos: 0, 17161 17162 /** 17163 * Google Caja version number or 0. 17164 * @property caja 17165 * @type float 17166 */ 17167 caja: nav && nav.cajaVersion, 17168 17169 /** 17170 * Set to true if the page appears to be in SSL 17171 * @property secure 17172 * @type boolean 17173 * @static 17174 */ 17175 secure: false, 17176 17177 /** 17178 * The operating system. Currently only detecting windows or macintosh 17179 * @property os 17180 * @type string 17181 * @static 17182 */ 17183 os: null 17184 17185 }, 17186 17187 ua = agent || (navigator && navigator.userAgent), 17188 17189 loc = window && window.location, 17190 17191 href = loc && loc.href, 17192 17193 m; 17194 17195 o.secure = href && (href.toLowerCase().indexOf("https") === 0); 17196 17197 if (ua) { 17198 17199 if ((/windows|win32/i).test(ua)) { 17200 o.os = 'windows'; 17201 } else if ((/macintosh/i).test(ua)) { 17202 o.os = 'macintosh'; 17203 } else if ((/rhino/i).test(ua)) { 17204 o.os = 'rhino'; 17205 } 17206 17207 // Modern KHTML browsers should qualify as Safari X-Grade 17208 if ((/KHTML/).test(ua)) { 17209 o.webkit = 1; 17210 } 17211 // Modern WebKit browsers are at least X-Grade
17212 m = ua.match(/AppleWebKit\/([^\s]*)/); 17213 if (m && m[1]) { 17214 o.webkit = numberify(m[1]); 17215 17216 // Mobile browser check 17217 if (/ Mobile\//.test(ua)) { 17218 o.mobile = 'Apple'; // iPhone or iPod Touch 17219 17220 m = ua.match(/OS ([^\s]*)/); 17221 if (m && m[1]) { 17222 m = numberify(m[1].replace('_', '.')); 17223 } 17224 o.ios = m; 17225 o.ipad = o.ipod = o.iphone = 0; 17226 17227 m = ua.match(/iPad|iPod|iPhone/); 17228 if (m && m[0]) { 17229 o[m[0].toLowerCase()] = o.ios; 17230 } 17231 } else { 17232 m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/); 17233 if (m) { 17234 // Nokia N-series, Android, webOS, ex: NokiaN95 17235 o.mobile = m[0]; 17236 } 17237 if (/webOS/.test(ua)) { 17238 o.mobile = 'WebOS'; 17239 m = ua.match(/webOS\/([^\s]*);/); 17240 if (m && m[1]) { 17241 o.webos = numberify(m[1]); 17242 } 17243 } 17244 if (/ Android/.test(ua)) { 17245 o.mobile = 'Android'; 17246 m = ua.match(/Android ([^\s]*);/); 17247 if (m && m[1]) { 17248 o.android = numberify(m[1]); 17249 } 17250 17251 } 17252 } 17253 17254 m = ua.match(/Chrome\/([^\s]*)/); 17255 if (m && m[1]) { 17256 o.chrome = numberify(m[1]); // Chrome 17257 } else { 17258 m = ua.match(/AdobeAIR\/([^\s]*)/); 17259 if (m) { 17260 o.air = m[0]; // Adobe AIR 1.0 or better 17261 } 17262 } 17263 } 17264 17265 if (!o.webkit) { // not webkit 17266// @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr) 17267 m = ua.match(/Opera[\s\/]([^\s]*)/); 17268 if (m && m[1]) { 17269 o.opera = numberify(m[1]); 17270 m = ua.match(/Version\/([^\s]*)/); 17271 if (m && m[1]) { 17272 o.opera = numberify(m[1]); // opera 10+ 17273 } 17274 m = ua.match(/Opera Mini[^;]*/); 17275 if (m) { 17276 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316 17277 } 17278 } else { // not opera or webkit 17279 m = ua.match(/MSIE ([^;]*)|Trident.*; rv.([0-9.]+)/); 17280 if (m && (m[1] || m[2])) { 17281 o.ie = numberify(m[1] || m[2]); 17282 } else { // not opera, webkit, or ie 17283 m = ua.match(/Gecko\/([^\s]*)/); 17284 if (m) { 17285 o.gecko = 1; // Gecko detected, look for revision 17286 m = ua.match(/rv:([^\s\)]*)/); 17287 if (m && m[1]) { 17288 o.gecko = numberify(m[1]); 17289 } 17290 } 17291 } 17292 } 17293 } 17294 } 17295 17296 return o; 17297}; 17298 17299YAHOO.env.ua = YAHOO.env.parseUA(); 17300 17301/* 17302 * Initializes the global by creating the default namespaces and applying 17303 * any new configuration information that is detected. This is the setup 17304 * for env. 17305 * @method init 17306 * @static 17307 * @private 17308 */ 17309(function() { 17310 YAHOO.namespace("util", "widget", "example"); 17311 /*global YAHOO_config*/ 17312 if ("undefined" !== typeof YAHOO_config) { 17313 var l=YAHOO_config.listener, ls=YAHOO.env.listeners,unique=true, i; 17314 if (l) { 17315 // if YAHOO is loaded multiple times we need to check to see if 17316 // this is a new config object. If it is, add the new component 17317 // load listener to the stack 17318 for (i=0; i<ls.length; i++) { 17319 if (ls[i] == l) { 17320 unique = false; 17321 break; 17322 } 17323 } 17324 17325 if (unique) { 17326 ls.push(l); 17327 } 17328 } 17329 } 17330})(); 17331/** 17332 * Provides the language utilites and extensions used by the library 17333 * @class YAHOO.lang 17334 */ 17335YAHOO.lang = YAHOO.lang || {}; 17336 17337(function() { 17338 17339 17340var L = YAHOO.lang, 17341 17342 OP = Object.prototype, 17343 ARRAY_TOSTRING = '[object Array]', 17344 FUNCTION_TOSTRING = '[object Function]', 17345 OBJECT_TOSTRING = '[object Object]', 17346 NOTHING = [], 17347 17348 HTML_CHARS = { 17349 '&': '&', 17350 '<': '<', 17351 '>': '>', 17352 '"': '"', 17353 "'": ''', 17354 '/': '/', 17355 '`': '`' 17356 }, 17357 17358 // ADD = ["toString", "valueOf", "hasOwnProperty"], 17359 ADD = ["toString", "valueOf"], 17360 17361 OB = { 17362 17363 /** 17364 * Determines wheather or not the provided object is an array.
17365 * @method isArray 17366 * @param {any} o The object being testing 17367 * @return {boolean} the result 17368 */ 17369 isArray: function(o) { 17370 return OP.toString.apply(o) === ARRAY_TOSTRING; 17371 }, 17372 17373 /** 17374 * Determines whether or not the provided object is a boolean 17375 * @method isBoolean 17376 * @param {any} o The object being testing 17377 * @return {boolean} the result 17378 */ 17379 isBoolean: function(o) { 17380 return typeof o === 'boolean'; 17381 }, 17382 17383 /** 17384 * Determines whether or not the provided object is a function. 17385 * Note: Internet Explorer thinks certain functions are objects: 17386 * 17387 * var obj = document.createElement("object"); 17388 * YAHOO.lang.isFunction(obj.getAttribute) // reports false in IE 17389 * 17390 * var input = document.createElement("input"); // append to body 17391 * YAHOO.lang.isFunction(input.focus) // reports false in IE 17392 * 17393 * You will have to implement additional tests if these functions 17394 * matter to you. 17395 * 17396 * @method isFunction 17397 * @param {any} o The object being testing 17398 * @return {boolean} the result 17399 */ 17400 isFunction: function(o) { 17401 return (typeof o === 'function') || OP.toString.apply(o) === FUNCTION_TOSTRING; 17402 }, 17403 17404 /** 17405 * Determines whether or not the provided object is null 17406 * @method isNull 17407 * @param {any} o The object being testing 17408 * @return {boolean} the result 17409 */ 17410 isNull: function(o) { 17411 return o === null; 17412 }, 17413 17414 /** 17415 * Determines whether or not the provided object is a legal number 17416 * @method isNumber 17417 * @param {any} o The object being testing 17418 * @return {boolean} the result 17419 */ 17420 isNumber: function(o) { 17421 return typeof o === 'number' && isFinite(o); 17422 }, 17423 17424 /** 17425 * Determines whether or not the provided object is of type object 17426 * or function 17427 * @method isObject 17428 * @param {any} o The object being testing 17429 * @return {boolean} the result 17430 */ 17431 isObject: function(o) { 17432return (o && (typeof o === 'object' || L.isFunction(o))) || false; 17433 }, 17434 17435 /** 17436 * Determines whether or not the provided object is a string 17437 * @method isString 17438 * @param {any} o The object being testing 17439 * @return {boolean} the result 17440 */ 17441 isString: function(o) { 17442 return typeof o === 'string'; 17443 }, 17444 17445 /** 17446 * Determines whether or not the provided object is undefined 17447 * @method isUndefined 17448 * @param {any} o The object being testing 17449 * @return {boolean} the result 17450 */ 17451 isUndefined: function(o) { 17452 return typeof o === 'undefined'; 17453 }, 17454 17455 17456 /** 17457 * IE will not enumerate native functions in a derived object even if the 17458 * function was overridden. This is a workaround for specific functions 17459 * we care about on the Object prototype. 17460 * @property _IEEnumFix 17461 * @param {Function} r the object to receive the augmentation 17462 * @param {Function} s the object that supplies the properties to augment 17463 * @static 17464 * @private 17465 */ 17466 _IEEnumFix: (YAHOO.env.ua.ie) ? function(r, s) { 17467 var i, fname, f; 17468 for (i=0;i<ADD.length;i=i+1) { 17469 17470 fname = ADD[i]; 17471 f = s[fname]; 17472 17473 if (L.isFunction(f) && f!=OP[fname]) { 17474 r[fname]=f; 17475 } 17476 } 17477 } : function(){}, 17478 17479 /** 17480 * <p> 17481 * Returns a copy of the specified string with special HTML characters 17482 * escaped. The following characters will be converted to their 17483 * corresponding character entities: 17484 * <code>& < > " ' / `</code> 17485 * </p> 17486 * 17487 * <p> 17488 * This implementation is based on the 17489 * <a href="http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet">OWASP 17490 * HTML escaping recommendations</a>. In addition to the characters 17491 * in the OWASP recommendation, we also escape the <code>`</code> 17492 * character, since IE interprets it as an attribute delimiter when used in 17493 * innerHTML. 17494 * </p> 17495 * 17496 * @method escapeHTML 17497 * @param {String} html String to escape. 17498 * @return {String} Escaped string. 17499 * @static 17500 * @since 2.9.0 17501 */ 17502 escapeHTML: function (html) { 17503 return html.replace(/[&<>"'\/`]/g, function (match) { 17504 return HTML_CHARS[match]; 17505 }); 17506 }, 17507 17508 /** 17509 * Utility to set up the prototype, constructor and superclass properties to 17510 * support an inheritance strategy that can chain constructors and methods. 17511 * Static members will not be inherited. 17512 * 17513 * @method extend 17514 * @static 17515 * @param {Function} subc the object to modify 17516 * @param {Function} superc the object to inherit 17517 * @param {Object} overrides additional properties/methods to add to the 17518 * subclass prototype. These will override the 17519 * matching items obtained from the superclass 17520 * if present. 17521 */ 17522 extend: function(subc, superc, overrides) { 17523 if (!superc||!subc) { 17524 throw new Error("extend failed, please check that " + 17525 "all dependencies are included."); 17526 } 17527 var F = function() {}, i; 17528 F.prototype=superc.prototype; 17529 subc.prototype=new F(); 17530 subc.prototype.constructor=subc; 17531 subc.superclass=superc.prototype; 17532 if (superc.prototype.constructor == OP.constructor) { 17533 superc.prototype.constructor=superc; 17534 } 17535 17536 if (overrides) { 17537 for (i in overrides) { 17538 if (L.hasOwnProperty(overrides, i)) { 17539 subc.prototype[i]=overrides[i]; 17540 } 17541 } 17542 17543 L._IEEnumFix(subc.prototype, overrides); 17544 } 17545 }, 17546 17547 /** 17548 * Applies all properties in the supplier to the receiver if the 17549 * receiver does not have these properties yet. Optionally, one or 17550 * more methods/properties can be specified (as additional
17551 * parameters). This option will overwrite the property if receiver 17552 * has it already. If true is passed as the third parameter, all 17553 * properties will be applied and _will_ overwrite properties in 17554 * the receiver. 17555 * 17556 * @method augmentObject 17557 * @static 17558 * @since 2.3.0 17559 * @param {Function} r the object to receive the augmentation 17560 * @param {Function} s the object that supplies the properties to augment 17561 * @param {String*|boolean} arguments zero or more properties methods 17562 * to augment the receiver with. If none specified, everything 17563 * in the supplier will be used unless it would 17564 * overwrite an existing property in the receiver. If true 17565 * is specified as the third parameter, all properties will 17566 * be applied and will overwrite an existing property in 17567 * the receiver 17568 */ 17569 augmentObject: function(r, s) { 17570 if (!s||!r) { 17571 throw new Error("Absorb failed, verify dependencies."); 17572 } 17573 var a=arguments, i, p, overrideList=a[2]; 17574 if (overrideList && overrideList!==true) { // only absorb the specified properties 17575 for (i=2; i<a.length; i=i+1) { 17576 r[a[i]] = s[a[i]]; 17577 } 17578 } else { // take everything, overwriting only if the third parameter is true 17579 for (p in s) { 17580 if (overrideList || !(p in r)) { 17581 r[p] = s[p]; 17582 } 17583 } 17584 17585 L._IEEnumFix(r, s); 17586 } 17587 17588 return r; 17589 }, 17590 17591 /** 17592 * Same as YAHOO.lang.augmentObject, except it only applies prototype properties 17593 * @see YAHOO.lang.augmentObject 17594 * @method augmentProto 17595 * @static 17596 * @param {Function} r the object to receive the augmentation 17597 * @param {Function} s the object that supplies the properties to augment 17598 * @param {String*|boolean} arguments zero or more properties methods 17599 * to augment the receiver with. If none specified, everything 17600 * in the supplier will be used unless it would overwrite an existing 17601 * property in the receiver. if true is specified as the third 17602 * parameter, all properties will be applied and will overwrite an 17603 * existing property in the receiver 17604 */ 17605 augmentProto: function(r, s) { 17606 if (!s||!r) { 17607 throw new Error("Augment failed, verify dependencies."); 17608 } 17609 //var a=[].concat(arguments); 17610 var a=[r.prototype,s.prototype], i; 17611 for (i=2;i<arguments.length;i=i+1) { 17612 a.push(arguments[i]); 17613 } 17614 L.augmentObject.apply(this, a); 17615 17616 return r; 17617 }, 17618 17619 17620 /** 17621 * Returns a simple string representation of the object or array. 17622 * Other types of objects will be returned unprocessed. Arrays 17623 * are expected to be indexed. Use object notation for 17624 * associative arrays. 17625 * @method dump 17626 * @since 2.3.0 17627 * @param o {Object} The object to dump 17628 * @param d {int} How deep to recurse child objects, default 3 17629 * @return {String} the dump result 17630 */ 17631 dump: function(o, d) { 17632 var i,len,s=[],OBJ="{...}",FUN="f(){...}", 17633 COMMA=', ', ARROW=' => '; 17634 17635 // Cast non-objects to string 17636 // Skip dates because the std toString is what we want 17637 // Skip HTMLElement-like objects because trying to dump 17638 // an element will cause an unhandled exception in FF 2.x 17639 if (!L.isObject(o)) { 17640 return o + ""; 17641 } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) { 17642 return o; 17643 } else if (L.isFunction(o)) { 17644 return FUN; 17645 } 17646 17647 // dig into child objects the depth specifed. Default 3 17648 d = (L.isNumber(d)) ? d : 3; 17649 17650 // arrays [1, 2, 3] 17651 if (L.isArray(o)) { 17652 s.push("["); 17653 for (i=0,len=o.length;i<len;i=i+1) { 17654 if (L.isObject(o[i])) { 17655 s.push((d > 0) ? L.dump(o[i], d-1) : OBJ); 17656 } else { 17657 s.push(o[i]); 17658 } 17659 s.push(COMMA); 17660 } 17661 if (s.length > 1) { 17662 s.pop(); 17663 } 17664 s.push("]"); 17665 // objects {k1 => v1, k2 => v2} 17666 } else { 17667 s.push("{"); 17668 for (i in o) { 17669 if (L.hasOwnProperty(o, i)) { 17670 s.push(i + ARROW); 17671 if (L.isObject(o[i])) { 17672 s.push((d > 0) ? L.dump(o[i], d-1) : OBJ); 17673 } else { 17674 s.push(o[i]); 17675 } 17676 s.push(COMMA); 17677 } 17678 } 17679 if (s.length > 1) { 17680 s.pop(); 17681 } 17682 s.push("}"); 17683 } 17684 17685 return s.join(""); 17686 }, 17687 17688 /** 17689 * Does variable substitution on a string. It scans through the string 17690 * looking for expressions enclosed in { } braces. If an expression 17691 * is found, it is used a key on the object. If there is a space in 17692 * the key, the first word is used for the key and the rest is provided 17693 * to an optional function to be used to programatically determine the 17694 * value (the extra information might be used for this decision). If 17695 * the value for the key in the object, or what is returned from the 17696 * function has a string value, number value, or object value, it is 17697 * substituted for the bracket expression and it repeats. If this 17698 * value is an object, it uses the Object's toString() if this has 17699 * been overridden, otherwise it does a shallow dump of the key/value 17700 * pairs. 17701 * 17702 * By specifying the recurse option, the string is rescanned after 17703 * every replacement, allowing for nested template substitutions. 17704 * The side effect of this option is that curly braces in the 17705 * replacement content must be encoded. 17706 * 17707 * @method substitute 17708 * @since 2.3.0 17709 * @param s {String} The string that will be modified. 17710 * @param o {Object}
17710 An object containing the replacement values 17711 * @param f {Function} An optional function that can be used to 17712 * process each match. It receives the key, 17713 * value, and any extra metadata included with 17714 * the key inside of the braces. 17715 * @param recurse {boolean} default true - if not false, the replaced 17716 * string will be rescanned so that nested substitutions are possible. 17717 * @return {String} the substituted string 17718 */ 17719 substitute: function (s, o, f, recurse) { 17720 var i, j, k, key, v, meta, saved=[], token, lidx=s.length, 17721 DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}', 17722 dump, objstr; 17723 17724 for (;;) { 17725 i = s.lastIndexOf(LBRACE, lidx); 17726 if (i < 0) { 17727 break; 17728 } 17729 j = s.indexOf(RBRACE, i); 17730 if (i + 1 > j) { 17731 break; 17732 } 17733 17734 //Extract key and meta info 17735 token = s.substring(i + 1, j); 17736 key = token; 17737 meta = null; 17738 k = key.indexOf(SPACE); 17739 if (k > -1) { 17740 meta = key.substring(k + 1); 17741 key = key.substring(0, k); 17742 } 17743 17744 // lookup the value 17745 v = o[key]; 17746 17747 // if a substitution function was provided, execute it 17748 if (f) { 17749 v = f(key, v, meta); 17750 } 17751 17752 if (L.isObject(v)) { 17753 if (L.isArray(v)) { 17754 v = L.dump(v, parseInt(meta, 10)); 17755 } else { 17756 meta = meta || ""; 17757 17758 // look for the keyword 'dump', if found force obj dump 17759 dump = meta.indexOf(DUMP); 17760 if (dump > -1) { 17761 meta = meta.substring(4); 17762 } 17763 17764 objstr = v.toString(); 17765 17766 // use the toString if it is not the Object toString 17767 // and the 'dump' meta info was not found 17768 if (objstr === OBJECT_TOSTRING || dump > -1) { 17769 v = L.dump(v, parseInt(meta, 10)); 17770 } else { 17771 v = objstr; 17772 } 17773 } 17774 } else if (!L.isString(v) && !L.isNumber(v)) { 17775 // This {block} has no replace string. Save it for later. 17776 v = "~-" + saved.length + "-~"; 17777 saved[saved.length] = token; 17778 17779 // break; 17780 } 17781 17782 s = s.substring(0, i) + v + s.substring(j + 1); 17783 17784 if (recurse === false) { 17785 lidx = i-1; 17786 } 17787 17788 } 17789 17790 // restore saved {block}s 17791 for (i=saved.length-1; i>=0; i=i-1) { 17792 s = s.replace(new RegExp("~-" + i + "-~"), "{" + saved[i] + "}", "g"); 17793 } 17794 17795 return s; 17796 }, 17797 17798 17799 /** 17800 * Returns a string without any leading or trailing whitespace. If 17801 * the input is not a string, the input will be returned untouched. 17802 * @method trim 17803 * @since 2.3.0 17804 * @param s {string} the string to trim 17805 * @return {string} the trimmed string 17806 */ 17807 trim: function(s){ 17808 try { 17809 return s.replace(/^\s+|\s+$/g, ""); 17810 } catch(e) { 17811 return s; 17812 } 17813 }, 17814 17815 /** 17816 * Returns a new object containing all of the properties of 17817 * all the supplied objects. The properties from later objects 17818 * will overwrite those in earlier objects. 17819 * @method merge 17820 * @since 2.3.0 17821 * @param arguments {Object*} the objects to merge 17822 * @return the new merged object 17823 */ 17824 merge: function() { 17825 var o={}, a=arguments, l=a.length, i; 17826 for (i=0; i<l; i=i+1) { 17827 L.augmentObject(o, a[i], true); 17828 } 17829 return o; 17830 }, 17831 17832 /** 17833 * Executes the supplied function in the context of the supplied 17834 * object 'when' milliseconds later. Executes the function a 17835 * single time unless periodic is set to true. 17836 * @method later 17837 * @since 2.4.0 17838 * @param when {int} the number of milliseconds to wait until the fn 17839 * is executed 17840 * @param o the context object 17841 * @param fn {Function|String} the function to execute or the name of 17842 * the method in the 'o' object to execute 17843 * @param data [Array] data that is provided to the function. This accepts 17844 * either a single item or an array. If an array is provided, the 17845 * function is executed with one parameter for each array item. If 17846 * you need to pass a single array parameter, it needs to be wrapped in 17847 * an array [myarray] 17848 * @param periodic {boolean} if true, executes continuously at supplied 17849 * interval until canceled 17850 * @return a timer object. Call the cancel() method on this object to 17851 * stop the timer. 17852 */ 17853 later: function(when, o, fn, data, periodic) { 17854 when = when || 0; 17855 o = o || {}; 17856 var m=fn, d=data, f, r; 17857 17858 if (L.isString(fn)) { 17859 m = o[fn]; 17860 } 17861 17862 if (!m) { 17863 throw new TypeError("method undefined"); 17864 } 17865 17866 if (!L.isUndefined(data) && !L.isArray(d)) { 17867 d = [data]; 17868 } 17869 17870 f = function() { 17871 m.apply(o, d || NOTHING); 17872 }; 17873 17874 r = (periodic) ? setInterval(f, when) : setTimeout(f, when); 17875 17876 return { 17877 interval: periodic, 17878 cancel: function() { 17879 if (this.interval) { 17880 clearInterval(r); 17881 } else { 17882 clearTimeout(r); 17883 } 17884 } 17885 }; 17886 }, 17887 17888 /** 17889 * A convenience method for detecting a legitimate non-null value. 17890 * Returns false for null/undefined/NaN, true for other values, 17891 * including 0/false/'' 17892 * @method isValue 17893 * @since 2.3.0 17894 * @param o {any} the item to test 17895 * @return {boolean} true if it is not null/undefined/NaN || false 17896 */ 17897 isValue: function(o) { 17898 // return (o || o === false || o === 0 || o === ''); // Infinity fails 17899return (L.isObject(o) || L.isString(o) || L.isNumber(o) || L.isBoolean(o)); 17900 } 17901 17902}; 17903 17904/** 17905 * Determines whether or not the property was added 17906 * to the object instance. Returns false if the property is not present 17907 * in the object, or was inherited from the prototype. 17908 * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x. 17909 * There is a discrepancy between YAHOO.lang.hasOwnProperty and 17910 * Object.prototype.hasOwnProperty when the property is a primitive added to 17911 * both the instance AND prototype with the same value: 17912 * <pre> 17913 * var A = function() {}; 17914 * A.prototype.foo = 'foo'; 17915 * var a = new A(); 17916 * a.foo = 'foo'; 17917 * alert(a.hasOwnProperty('foo')); // true 17918 * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback 17919 * </pre> 17920 * @method hasOwnProperty 17921 * @param {any} o The object being testing 17922 * @param prop {string} the name of the property to test 17923 * @return {boolean} the result 17924 */ 17925L.hasOwnProperty = (OP.hasOwnProperty) ? 17926 function(o, prop) { 17927 return o && o.hasOwnProperty && o.hasOwnProperty(prop); 17928 } : function(o, prop) { 17929 return !L.isUndefined(o[prop]) && 17930 o.constructor.prototype[prop] !== o[prop]; 17931 }; 17932 17933// new lang wins 17934OB.augmentObject(L, OB, true); 17935 17936/* 17937 * An alias for <a href="YAHOO.lang.html">YAHOO.lang</a> 17938 * @class YAHOO.util.Lang 17939 */ 17940YAHOO.util.Lang = L; 17941 17942/** 17943 * Same as YAHOO.lang.augmentObject, except it only applies prototype 17944 * properties. This is an alias for augmentProto. 17945 * @see YAHOO.lang.augmentObject 17946 * @method augment 17947 * @static 17948 * @param {Function} r the object to receive the augmentation 17949 * @param {Function} s the object that supplies the properties to augment 17950 * @param {String*|boolean} arguments zero or more properties methods to 17951 * augment the receiver with. If none specified, everything 17952 * in the supplier will be used unless it would 17953 * overwrite an existing property in the receiver. if true 17954 * is specified as the third parameter, all properties will
17955 * be applied and will overwrite an existing property in 17956 * the receiver 17957 */ 17958L.augment = L.augmentProto; 17959 17960/** 17961 * An alias for <a href="YAHOO.lang.html#augment">YAHOO.lang.augment</a> 17962 * @for YAHOO 17963 * @method augment 17964 * @static 17965 * @param {Function} r the object to receive the augmentation 17966 * @param {Function} s the object that supplies the properties to augment 17967 * @param {String*} arguments zero or more properties methods to 17968 * augment the receiver with. If none specified, everything 17969 * in the supplier will be used unless it would 17970 * overwrite an existing property in the receiver 17971 */ 17972YAHOO.augment = L.augmentProto; 17973 17974/** 17975 * An alias for <a href="YAHOO.lang.html#extend">YAHOO.lang.extend</a> 17976 * @method extend 17977 * @static 17978 * @param {Function} subc the object to modify 17979 * @param {Function} superc the object to inherit 17980 * @param {Object} overrides additional properties/methods to add to the 17981 * subclass prototype. These will override the 17982 * matching items obtained from the superclass if present. 17983 */ 17984YAHOO.extend = L.extend; 17985 17986})(); 17987YAHOO.register("yahoo", YAHOO, {version: "2.9.0", build: "2800"}); 17988/** 17989 * Provides a mechanism to fetch remote resources and 17990 * insert them into a document 17991 * This utility can fetch JavaScript and CSS files, inserting script 17992 * tags for script and link tags for CSS. Note, this 17993 * is done via the normal browser mechanisms for inserting 17994 * these resources and making the content available to 17995 * code that would access it. Be careful when retreiving 17996 * remote resources. Only use this utility to fetch 17997 * files from sites you trust. 17998 * 17999 * @module get 18000 * @requires yahoo 18001 */ 18002 18003/** 18004 * Fetches and inserts one or more script or link nodes into the document. 18005 * This utility can fetch JavaScript and CSS files, inserting script 18006 * tags for script and link tags for CSS. Note, this 18007 * is done via the normal browser mechanisms for inserting 18008 * these resources and making the content available to 18009 * code that would access it. Be careful when retreiving 18010 * remote resources. Only use this utility to fetch 18011 * files from sites you trust. 18012 * 18013 * @namespace YAHOO.util 18014 * @class YAHOO.util.Get 18015 */ 18016YAHOO.util.Get = function() { 18017 18018 /** 18019 * hash of queues to manage multiple requests 18020 * @property queues 18021 * @private 18022 */ 18023 var queues={}, 18024 18025 /** 18026 * queue index used to generate transaction ids 18027 * @property qidx 18028 * @type int 18029 * @private 18030 */ 18031 qidx=0, 18032 18033 /** 18034 * node index used to generate unique node ids 18035 * @property nidx 18036 * @type int 18037 * @private 18038 */ 18039 nidx=0, 18040 18041 /** 18042 * interal property used to prevent multiple simultaneous purge 18043 * processes 18044 * @property purging 18045 * @type boolean 18046 * @private 18047 */ 18048 _purging=false, 18049 18050 ua=YAHOO.env.ua, 18051 18052 lang=YAHOO.lang, 18053 18054 _fail, 18055 _purge, 18056 _track, 18057 18058 /** 18059 * Generates an HTML element, this is not appended to a document 18060 * @method _node 18061 * @param type {string} the type of element 18062 * @param attr {string} the attributes 18063 * @param win {Window} optional window to create the element in 18064 * @return {HTMLElement} the generated node 18065 * @private 18066 */ 18067 _node = function(type, attr, win) { 18068 var w = win || window, d=w.document, n=d.createElement(type), i; 18069 18070 for (i in attr) { 18071 if (attr.hasOwnProperty(i)) { 18072 n.setAttribute(i, attr[i]); 18073 } 18074 } 18075 18076 return n; 18077 }, 18078 18079 /** 18080 * Generates a link node 18081 * @method _linkNode 18082 * @param url {string} the url for the css file 18083 * @param win {Window} optional window to create the node in 18084 * @return {HTMLElement} the generated node 18085 * @private 18086 */ 18087 _linkNode = function(url, win, attributes) { 18088 18089 var o = { 18090 id: "yui__dyn_" + (nidx++), 18091 type: "text/css", 18092 rel: "stylesheet", 18093 href: url 18094 }; 18095 18096 if (attributes) { 18097 lang.augmentObject(o, attributes); 18098 } 18099 18100 return _node("link", o, win); 18101 }, 18102 18103 /** 18104 * Generates a script node 18105 * @method _scriptNode 18106 * @param url {string} the url for the script file 18107 * @param win {Window} optional window to create the node in 18108 * @return {HTMLElement} the generated node 18109 * @private 18110 */ 18111 _scriptNode = function(url, win, attributes) { 18112 var o = { 18113 id: "yui__dyn_" + (nidx++), 18114 type: "text/javascript", 18115 src: url 18116 }; 18117 18118 if (attributes) { 18119 lang.augmentObject(o, attributes); 18120 } 18121 18122 return _node("script", o, win); 18123 }, 18124 18125 /** 18126 * Returns the data payload for callback functions 18127 * @method _returnData 18128 * @private 18129 */ 18130 _returnData = function(q, msg) { 18131 return { 18132 tId: q.tId, 18133 win: q.win, 18134 data: q.data, 18135 nodes: q.nodes, 18136 msg: msg, 18137 purge: function() { 18138 _purge(this.tId); 18139 } 18140 }; 18141 }, 18142 18143 _get = function(nId, tId) { 18144 var q = queues[tId], 18145 n = (lang.isString(nId)) ? q.win.document.getElementById(nId) : nId; 18146 if (!n) { 18147 _fail(tId, "target node not found: " + nId); 18148 } 18149 18150 return n; 18151 }, 18152 18153 18154 /** 18155 * The request is complete, so executing the requester's callback 18156 * @method _finish 18157 * @param id {string} the id of the request 18158 * @private 18159 */ 18160 _finish = function(id) { 18161 YAHOO.log("Finishing transaction " + id); 18162 var q = queues[id], msg, context; 18163 q.finished = true; 18164 18165 if (q.aborted) { 18166 msg = "transaction " + id + " was aborted"; 18167 _fail(id, msg); 18168 return; 18169 } 18170 18171 // execute success callback 18172 if (q.onSuccess) { 18173 context = q.scope || q.win; 18174 q.onSuccess.call(context, _returnData(q)); 18175 } 18176 }, 18177 18178 /** 18179 * Timeout detected 18180 * @method _timeout 18181 * @param id {string} the id of the request 18182 * @private 18183 */ 18184 _timeout = function(id) { 18185 YAHOO.log("Timeout " + id, "info", "get"); 18186 var q = queues[id], context; 18187 if (q.onTimeout) { 18188 context = q.scope || q; 18189 q.onTimeout.call(context, _returnData(q)); 18190 } 18191 }, 18192 18193 /** 18194 * Loads the next item for a given request 18195 * @method _next 18196 * @param id {string} the id of the request 18197 * @param loaded {string} the url that was just loaded, if any 18198 * @private 18199 */ 18200 _next = function(id, loaded) { 18201 18202 YAHOO.log("_next: " + id + ", loaded: " + loaded, "info", "Get"); 18203 18204 var q = queues[id], w=q.win, d=w.document, h=d.getElementsByTagName("head")[0], 18205 n, msg, url, s, extra; 18206 18207 if (q.timer) { 18208 // Y.log('cancel timer'); 18209 q.timer.cancel(); 18210 } 18211 18212 if (q.aborted) { 18213 msg = "transaction " + id + " was aborted"; 18214 _fail(id, msg); 18215 return; 18216 } 18217 18218 if (loaded) {
18219 q.url.shift(); 18220 if (q.varName) { 18221 q.varName.shift(); 18222 } 18223 } else { 18224 // This is the first pass: make sure the url is an array 18225 q.url = (lang.isString(q.url)) ? [q.url] : q.url; 18226 if (q.varName) { 18227 q.varName = (lang.isString(q.varName)) ? [q.varName] : q.varName; 18228 } 18229 } 18230 18231 18232 if (q.url.length === 0) { 18233 // Safari 2.x workaround - There is no way to know when 18234 // a script is ready in versions of Safari prior to 3.x. 18235 // Adding an extra node reduces the problem, but doesn't 18236 // eliminate it completely because the browser executes 18237 // them asynchronously. 18238 if (q.type === "script" && ua.webkit && ua.webkit < 420 && 18239 !q.finalpass && !q.varName) { 18240 // Add another script node. This does not guarantee that the 18241 // scripts will execute in order, but it does appear to fix the 18242 // problem on fast connections more effectively than using an 18243 // arbitrary timeout. It is possible that the browser does 18244 // block subsequent script execution in this case for a limited 18245 // time. 18246 extra = _scriptNode(null, q.win, q.attributes); 18247 extra.innerHTML='YAHOO.util.Get._finalize("' + id + '");'; 18248 q.nodes.push(extra); h.appendChild(extra); 18249 18250 } else { 18251 _finish(id); 18252 } 18253 18254 return; 18255 } 18256 18257 18258 url = q.url[0]; 18259 18260 // if the url is undefined, this is probably a trailing comma problem in IE 18261 if (!url) {
18262 q.url.shift(); 18263 YAHOO.log('skipping empty url'); 18264 return _next(id); 18265 } 18266 18267 YAHOO.log("attempting to load " + url, "info", "Get"); 18268 18269 if (q.timeout) { 18270 // Y.log('create timer'); 18271 q.timer = lang.later(q.timeout, q, _timeout, id); 18272 } 18273 18274 if (q.type === "script") { 18275 n = _scriptNode(url, w, q.attributes); 18276 } else { 18277 n = _linkNode(url, w, q.attributes); 18278 } 18279 18280 // track this node's load progress 18281 _track(q.type, n, id, url, w, q.url.length); 18282 18283 // add the node to the queue so we can return it to the user supplied callback 18284 q.nodes.push(n); 18285 18286 // add it to the head or insert it before 'insertBefore' 18287 if (q.insertBefore) { 18288 s = _get(q.insertBefore, id); 18289 if (s) { 18290 s.parentNode.insertBefore(n, s); 18291 } 18292 } else { 18293 h.appendChild(n); 18294 } 18295 18296 YAHOO.log("Appending node: " + url, "info", "Get"); 18297 18298 // FireFox does not support the onload event for link nodes, so there is 18299 // no way to make the css requests synchronous. This means that the css 18300 // rules in multiple files could be applied out of order in this browser 18301 // if a later request returns before an earlier one. Safari too. 18302 if ((ua.webkit || ua.gecko) && q.type === "css") { 18303 _next(id, url); 18304 } 18305 }, 18306 18307 /** 18308 * Removes processed queues and corresponding nodes 18309 * @method _autoPurge 18310 * @private 18311 */ 18312 _autoPurge = function() { 18313 18314 if (_purging) { 18315 return; 18316 } 18317 18318 _purging = true; 18319 18320 var i, q; 18321 18322 for (i in queues) { 18323 if (queues.hasOwnProperty(i)) { 18324 q = queues[i]; 18325 if (q.autopurge && q.finished) { 18326 _purge(q.tId); 18327 delete queues[i]; 18328 } 18329 } 18330 } 18331 18332 _purging = false; 18333 }, 18334 18335 /** 18336 * Saves the state for the request and begins loading 18337 * the requested urls 18338 * @method queue 18339 * @param type {string} the type of node to insert 18340 * @param url {string} the url to load 18341 * @param opts the hash of options for this request 18342 * @private 18343 */ 18344 _queue = function(type, url, opts) { 18345 18346 var id = "q" + (qidx++), q; 18347 opts = opts || {}; 18348 18349 if (qidx % YAHOO.util.Get.PURGE_THRESH === 0) { 18350 _autoPurge(); 18351 } 18352 18353 queues[id] = lang.merge(opts, { 18354 tId: id, 18355 type: type, 18356 url: url, 18357 finished: false, 18358 aborted: false, 18359 nodes: [] 18360 }); 18361 18362 q = queues[id]; 18363 q.win = q.win || window; 18364 q.scope = q.scope || q.win; 18365 q.autopurge = ("autopurge" in q) ? q.autopurge : 18366 (type === "script") ? true : false; 18367 18368 q.attributes = q.attributes || {}; 18369 q.attributes.charset = opts.charset || q.attributes.charset || 'utf-8'; 18370 18371 lang.later(0, q, _next, id); 18372 18373 return { 18374 tId: id 18375 }; 18376 }; 18377 18378 /** 18379 * Detects when a node has been loaded. In the case of 18380 * script nodes, this does not guarantee that contained 18381 * script is ready to use. 18382 * @method _track 18383 * @param type {string} the type of node to track 18384 * @param n {HTMLElement} the node to track 18385 * @param id {string} the id of the request 18386 * @param url {string} the url that is being loaded 18387 * @param win {Window} the targeted window 18388 * @param qlength the number of remaining items in the queue, 18389 * including this one 18390 * @param trackfn {Function} function to execute when finished 18391 * the default is _next 18392 * @private 18393 */ 18394 _track = function(type, n, id, url, win, qlength, trackfn) { 18395 var f = trackfn || _next, rs, q, a, freq, w, l, i, msg; 18396 18397 // IE supports the readystatechange event for script and css nodes 18398 if (ua.ie && ua.ie < 11) { 18399 n.onreadystatechange = function() { 18400 rs = this.readyState; 18401 if ("loaded" === rs || "complete" === rs) { 18402 YAHOO.log(id + " onload " + url, "info", "Get"); 18403 n.onreadystatechange = null; 18404 f(id, url); 18405 } 18406 }; 18407 18408 // webkit prior to 3.x is problemmatic 18409 } else if (ua.webkit) { 18410 18411 if (type === "script") { 18412
18413 // Safari 3.x supports the load event for script nodes (DOM2) 18414 if (ua.webkit >= 420) { 18415 18416 n.addEventListener("load", function() { 18417 YAHOO.log(id + " DOM2 onload " + url, "info", "Get"); 18418 f(id, url); 18419 }); 18420 18421 // Nothing can be done with Safari < 3.x except to pause and hope 18422 // for the best, particularly after last script is inserted. The 18423 // scripts will always execute in the order they arrive, not 18424 // necessarily the order in which they were inserted. To support 18425 // script nodes with complete reliability in these browsers, script 18426 // nodes either need to invoke a function in the window once they 18427 // are loaded or the implementer needs to provide a well-known 18428 // property that the utility can poll for. 18429 } else { 18430 // Poll for the existence of the named variable, if it 18431 // was supplied. 18432 q = queues[id]; 18433 if (q.varName) { 18434 freq = YAHOO.util.Get.POLL_FREQ; 18435 YAHOO.log("Polling for " + q.varName[0]); 18436 q.maxattempts = YAHOO.util.Get.TIMEOUT/freq; 18437 q.attempts = 0; 18438 q._cache = q.varName[0].split("."); 18439 q.timer = lang.later(freq, q, function(o) { 18440 a = this._cache; 18441 l = a.length; 18442 w = this.win; 18443 for (i=0; i<l; i=i+1) { 18444 w = w[a[i]]; 18445 if (!w) { 18446 // if we have exausted our attempts, give up 18447 this.attempts++; 18448 if (this.attempts++ > this.maxattempts) { 18449 msg = "Over retry limit, giving up"; 18450 q.timer.cancel(); 18451 _fail(id, msg); 18452 } else { 18453 YAHOO.log(a[i] + " failed, retrying"); 18454 } 18455 return; 18456 } 18457 } 18458 18459 YAHOO.log("Safari poll complete"); 18460 18461 q.timer.cancel(); 18462 f(id, url); 18463 18464 }, null, true); 18465 } else { 18466 lang.later(YAHOO.util.Get.POLL_FREQ, null, f, [id, url]); 18467 } 18468 } 18469 } 18470
18471 // FireFox and Opera support onload (but not DOM2 in FF) handlers for 18472 // script nodes. Opera, but not FF, supports the onload event for link 18473 // nodes. 18474 } else { 18475 n.onload = function() { 18476 YAHOO.log(id + " onload " + url, "info", "Get"); 18477 f(id, url); 18478 }; 18479 } 18480 }; 18481 18482 /* 18483 * The request failed, execute fail handler with whatever 18484 * was accomplished. There isn't a failure case at the 18485 * moment unless you count aborted transactions 18486 * @method _fail 18487 * @param id {string} the id of the request 18488 * @private 18489 */ 18490 _fail = function(id, msg) { 18491 YAHOO.log("get failure: " + msg, "warn", "Get"); 18492 var q = queues[id], context; 18493 // execute failure callback 18494 if (q.onFailure) { 18495 context = q.scope || q.win; 18496 q.onFailure.call(context, _returnData(q, msg)); 18497 } 18498 }; 18499 18500 /** 18501 * Removes the nodes for the specified queue 18502 * @method _purge 18503 * @private 18504 */ 18505 _purge = function(tId) { 18506 if (queues[tId]) { 18507 18508 var q = queues[tId], 18509 nodes = q.nodes, 18510 l = nodes.length, 18511 d = q.win.document, 18512 h = d.getElementsByTagName("head")[0], 18513 sib, i, node, attr; 18514 18515 if (q.insertBefore) { 18516 sib = _get(q.insertBefore, tId); 18517 if (sib) { 18518 h = sib.parentNode; 18519 } 18520 } 18521 18522 for (i=0; i<l; i=i+1) { 18523 node = nodes[i]; 18524 if (node.clearAttributes) { 18525 node.clearAttributes(); 18526 } else { 18527 for (attr in node) { 18528 if (node.hasOwnProperty(attr)) { 18529 delete node[attr]; 18530 } 18531 } 18532 } 18533 18534 h.removeChild(node); 18535 } 18536 18537 q.nodes = []; 18538 } 18539 }; 18540 18541 18542 return { 18543 18544 /** 18545 * The default poll freqency in ms, when needed 18546 * @property POLL_FREQ 18547 * @static 18548 * @type int 18549 * @default 10 18550 */ 18551 POLL_FREQ: 10, 18552 18553 /** 18554 * The number of request required before an automatic purge. 18555 * property PURGE_THRESH 18556 * @static 18557 * @type int 18558 * @default 20 18559 */ 18560 PURGE_THRESH: 20, 18561 18562 /** 18563 * The length time to poll for varName when loading a script in 18564 * Safari 2.x before the transaction fails. 18565 * property TIMEOUT 18566 * @static 18567 * @type int 18568 * @default 2000 18569 */ 18570 TIMEOUT: 2000, 18571 18572 /** 18573 * Called by the the helper for detecting script load in Safari 18574 * @method _finalize 18575 * @param id {string} the transaction id 18576 * @private 18577 */ 18578 _finalize: function(id) { 18579 YAHOO.log(id + " finalized ", "info", "Get"); 18580 lang.later(0, null, _finish, id); 18581 }, 18582 18583 /** 18584 * Abort a transaction 18585 * @method abort 18586 * @param {string|object} either the tId or the object returned from 18587 * script() or css() 18588 */ 18589 abort: function(o) { 18590 var id = (lang.isString(o)) ? o : o.tId, 18591 q = queues[id]; 18592 if (q) { 18593 YAHOO.log("Aborting " + id, "info", "Get"); 18594 q.aborted = true; 18595 } 18596 }, 18597 18598 /** 18599 * Fetches and inserts one or more script nodes into the head 18600 * of the current document or the document in a specified window. 18601 * 18602 * @method script 18603 * @static 18604 * @param url {string|string[]} the url or urls to the script(s) 18605 * @param opts {object} Options: 18606 * <dl> 18607 * <dt>onSuccess</dt> 18608 * <dd> 18609 * callback to execute when the script(s) are finished loading 18610 * The callback receives an object back with the following 18611 * data: 18612 * <dl> 18613 * <dt>win</dt> 18614 * <dd>the window the script(s) were inserted into</dd> 18615 * <dt>data</dt> 18616 * <dd>the data object passed in when the request was made</dd> 18617 * <dt>nodes</dt> 18618 * <dd>An array containing references to the nodes that were 18619 * inserted</dd> 18620 * <dt>purge</dt> 18621 * <dd>A function that, when executed, will remove the nodes 18622 * that were inserted</dd> 18623 * <dt> 18624 * </dl> 18625 * </dd> 18626 * <dt>onFailure</dt> 18627 * <dd> 18628 * callback to execute when the script load operation fails 18629 * The callback receives an object back with the following 18630 * data: 18631 * <dl> 18632 * <dt>win</dt> 18633 * <dd>the window the script(s) were inserted into</dd> 18634 * <dt>data</dt> 18635 * <dd>the data object passed in when the request was made</dd> 18636 * <dt>nodes</dt> 18637 * <dd>An array containing references to the nodes that were 18638 * inserted successfully</dd> 18639 * <dt>purge</dt> 18640 * <dd>A function that, when executed, will remove any nodes 18641 * that were inserted</dd> 18642 * <dt> 18643 * </dl> 18644 * </dd> 18645 * <dt>onTimeout</dt> 18646 * <dd> 18647 * callback to execute when a timeout occurs. 18648 * The callback receives an object back with the following 18649 * data: 18650 * <dl> 18651 * <dt>win</dt> 18652 * <dd>the window the script(s) were inserted into</dd> 18653 * <dt>data</dt> 18654 * <dd>the data object passed in when the request was made</dd> 18655 * <dt>nodes</dt> 18656 * <dd>An array containing references to the nodes that were 18657 * inserted</dd> 18658 * <dt>purge</dt> 18659 * <dd>A function that, when executed, will remove the nodes 18660 * that were inserted</dd> 18661 * <dt> 18662 * </dl> 18663 * </dd> 18664 * <dt>scope</dt> 18665 * <dd>the execution context for the callbacks</dd> 18666 * <dt>win</dt> 18667 * <dd>a window other than the one the utility occupies</dd> 18668 * <dt>autopurge</dt> 18669 * <dd> 18670 * setting to true will let the utilities cleanup routine purge 18671 * the script once loaded 18672 * </dd> 18673 * <dt>data</dt> 18674 * <dd> 18675 * data that is supplied to the callback when the script(s) are 18676 * loaded. 18677 * </dd> 18678 * <dt>varName</dt> 18679 * <dd> 18680 * variable that should be available when a script is finished 18681 * loading. Used to help Safari 2.x and below with script load 18682 * detection. The type of this property should match what was 18683 * passed into the url parameter: if loading a single url, a 18684 * string can be supplied. If loading multiple scripts, you 18685 * must supply an array that contains the variable name for 18686 * each script. 18687 * </dd> 18688 * <dt>insertBefore</dt> 18689 * <dd>node or node id that will become the new node's nextSibling</dd> 18690 * </dl> 18691 * <dt>charset</dt> 18692 * <dd>Node charset, deprecated, use 'attributes'</dd> 18693 * <dt>attributes</dt> 18694 * <dd>A hash of attributes to apply to dynamic nodes.</dd> 18695 * <dt>timeout</dt> 18696 * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd> 18697 * <pre> 18698 * // assumes yahoo, dom, and event are already on the page 18699 * YAHOO.util.Get.script( 18700 * ["http://yui.yahooapis.com/2.7.0/build/dragdrop/dragdrop-min.js", 18701 * "http://yui.yahooapis.com/2.7.0/build/animation/animation-min.js"], { 18702 * onSuccess: function(o) { 18703 * YAHOO.log(o.data); // foo 18704 * new YAHOO.util.DDProxy("dd1"); // also new o.reference("dd1"); would work 18705 * this.log("won't cause error because YAHOO is the scope"); 18706 * this.log(o.nodes.length === 2) // true 18707 * // o.purge(); // optionally remove the script nodes immediately 18708 * }, 18709 * onFailure: function(o) { 18710 * YAHOO.log("transaction failed"); 18711 * }, 18712 * data: "foo", 18713 * timeout: 10000, // 10 second timeout 18714 * scope: YAHOO, 18715 * // win: otherframe // target another window/frame 18716 * autopurge: true // allow the utility to choose when to remove the nodes 18717 * }); 18718 * </pre> 18719 * @return {tId: string}
18719 an object containing info about the transaction 18720 */ 18721 script: function(url, opts) { return _queue("script", url, opts); }, 18722 18723 /** 18724 * Fetches and inserts one or more css link nodes into the 18725 * head of the current document or the document in a specified 18726 * window. 18727 * @method css 18728 * @static 18729 * @param url {string} the url or urls to the css file(s) 18730 * @param opts Options: 18731 * <dl> 18732 * <dt>onSuccess</dt> 18733 * <dd> 18734 * callback to execute when the css file(s) are finished loading 18735 * The callback receives an object back with the following 18736 * data: 18737 * <dl>win</dl> 18738 * <dd>the window the link nodes(s) were inserted into</dd> 18739 * <dt>data</dt> 18740 * <dd>the data object passed in when the request was made</dd> 18741 * <dt>nodes</dt> 18742 * <dd>An array containing references to the nodes that were 18743 * inserted</dd> 18744 * <dt>purge</dt> 18745 * <dd>A function that, when executed, will remove the nodes 18746 * that were inserted</dd> 18747 * <dt> 18748 * </dl> 18749 * </dd> 18750 * <dt>scope</dt> 18751 * <dd>the execution context for the callbacks</dd> 18752 * <dt>win</dt> 18753 * <dd>a window other than the one the utility occupies</dd> 18754 * <dt>data</dt> 18755 * <dd> 18756 * data that is supplied to the callbacks when the nodes(s) are 18757 * loaded. 18758 * </dd> 18759 * <dt>insertBefore</dt> 18760 * <dd>node or node id that will become the new node's nextSibling</dd> 18761 * <dt>charset</dt> 18762 * <dd>Node charset, deprecated, use 'attributes'</dd> 18763 * <dt>attributes</dt> 18764 * <dd>A hash of attributes to apply to dynamic nodes.</dd> 18765 * </dl> 18766 * <pre> 18767 * YAHOO.util.Get.css("http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css"); 18768 * </pre> 18769 * <pre> 18770 * YAHOO.util.Get.css(["http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css", 18771 * "http://yui.yahooapis.com/2.7.0/build/logger/assets/skins/sam/logger.css"]); 18772 * </pre> 18773 * @return {tId: string}
18773 an object containing info about the transaction 18774 */ 18775 css: function(url, opts) { 18776 return _queue("css", url, opts); 18777 } 18778 }; 18779}(); 18780 18781YAHOO.register("get", YAHOO.util.Get, {version: "2.9.0", build: "2800"}); 18782/*jslint evil: true, strict: false, regexp: false*/ 18783 18784/** 18785 * Provides dynamic loading for the YUI library. It includes the dependency 18786 * info for the library, and will automatically pull in dependencies for 18787 * the modules requested. It supports rollup files (such as utilities.js 18788 * and yahoo-dom-event.js), and will automatically use these when 18789 * appropriate in order to minimize the number of http connections 18790 * required to load all of the dependencies. 18791 * 18792 * @module yuiloader 18793 * @namespace YAHOO.util 18794 */ 18795 18796/** 18797 * YUILoader provides dynamic loading for YUI. 18798 * @class YAHOO.util.YUILoader 18799 * @todo 18800 * version management, automatic sandboxing 18801 */ 18802(function() { 18803 18804 var Y = YAHOO, 18805 util = Y.util, 18806 lang = Y.lang, 18807 env = Y.env, 18808 PROV = "_provides", 18809 SUPER = "_supersedes", 18810 REQ = "expanded", 18811 AFTER = "_after", 18812 VERSION = "2.9.0"; 18813 18814 // version hack for cdn testing 18815 // if (/VERSION/.test(VERSION)) { 18816 // VERSION = "2.8.2"; 18817 // } 18818 18819 var YUI = { 18820 18821 dupsAllowed: {'yahoo': true, 'get': true}, 18822 18823 /* 18824 * The library metadata for the current release The is the default 18825 * value for YAHOO.util.YUILoader.moduleInfo 18826 * @property YUIInfo 18827 * @static 18828 */ 18829 info: { 18830 18831 // 'root': '2.5.2/build/', 18832 // 'base': 'http://yui.yahooapis.com/2.5.2/build/', 18833 18834 'root': VERSION + '/build/', 18835 'base': 'http://yui.yahooapis.com/' + VERSION + '/build/', 18836 18837 'comboBase': 'http://yui.yahooapis.com/combo?', 18838 18839 'skin': { 18840 'defaultSkin': 'sam', 18841 'base': 'assets/skins/', 18842 'path': 'skin.css', 18843 'after': ['reset', 'fonts', 'grids', 'base'], 18844 'rollup': 3 18845 }, 18846 18847 dupsAllowed: ['yahoo', 'get'], 18848 18849 'moduleInfo': { 18850 18851 'animation': { 18852 'type': 'js', 18853 'path': 'animation/animation-min.js', 18854 'requires': ['dom', 'event'] 18855 }, 18856 18857 'autocomplete': { 18858 'type': 'js', 18859 'path': 'autocomplete/autocomplete-min.js', 18860 'requires': ['dom', 'event', 'datasource'], 18861 'optional': ['connection', 'animation'], 18862 'skinnable': true 18863 }, 18864 18865 'base': { 18866 'type': 'css', 18867 'path': 'base/base-min.css', 18868 'after': ['reset', 'fonts', 'grids'] 18869 }, 18870 18871 'button': { 18872 'type': 'js', 18873 'path': 'button/button-min.js', 18874 'requires': ['element'], 18875 'optional': ['menu'], 18876 'skinnable': true 18877 }, 18878 18879 'calendar': { 18880 'type': 'js', 18881 'path': 'calendar/calendar-min.js', 18882 'requires': ['event', 'dom'], 18883 supersedes: ['datemath'], 18884 'skinnable': true 18885 }, 18886 18887 'carousel': { 18888 'type': 'js', 18889 'path': 'carousel/carousel-min.js', 18890 'requires': ['element'], 18891 'optional': ['animation'], 18892 'skinnable': true 18893 }, 18894 18895 'charts': { 18896 'type': 'js', 18897 'path': 'charts/charts-min.js', 18898 'requires': ['element', 'json', 'datasource', 'swf'] 18899 }, 18900 18901 'colorpicker': { 18902 'type': 'js', 18903 'path': 'colorpicker/colorpicker-min.js', 18904 'requires': ['slider', 'element'], 18905 'optional': ['animation'], 18906 'skinnable': true 18907 }, 18908 18909 'connection': { 18910 'type': 'js', 18911 'path': 'connection/connection-min.js', 18912 'requires': ['event'], 18913 'supersedes': ['connectioncore'] 18914 }, 18915 18916 'connectioncore': { 18917 'type': 'js', 18918 'path': 'connection/connection_core-min.js', 18919 'requires': ['event'], 18920 'pkg': 'connection' 18921 }, 18922 18923 'container': { 18924 'type': 'js', 18925 'path': 'container/container-min.js', 18926 'requires': ['dom', 'event'], 18927 // button is also optional, but this creates a circular 18928 // dependency when loadOptional is specified. button 18929 // optionally includes menu, menu requires container. 18930 'optional': ['dragdrop', 'animation', 'connection'], 18931 'supersedes': ['containercore'], 18932 'skinnable': true 18933 }, 18934 18935 'containercore': { 18936 'type': 'js',
18937 'path': 'container/container_core-min.js', 18938 'requires': ['dom', 'event'], 18939 'pkg': 'container' 18940 }, 18941 18942 'cookie': { 18943 'type': 'js', 18944 'path': 'cookie/cookie-min.js', 18945 'requires': ['yahoo'] 18946 }, 18947 18948 'datasource': { 18949 'type': 'js', 18950 'path': 'datasource/datasource-min.js', 18951 'requires': ['event'], 18952 'optional': ['connection'] 18953 }, 18954 18955 'datatable': { 18956 'type': 'js', 18957 'path': 'datatable/datatable-min.js', 18958 'requires': ['element', 'datasource'], 18959 'optional': ['calendar', 'dragdrop', 'paginator'], 18960 'skinnable': true 18961 }, 18962 18963 datemath: { 18964 'type': 'js', 18965 'path': 'datemath/datemath-min.js', 18966 'requires': ['yahoo'] 18967 }, 18968 18969 'dom': { 18970 'type': 'js', 18971 'path': 'dom/dom-min.js', 18972 'requires': ['yahoo'] 18973 }, 18974 18975 'dragdrop': { 18976 'type': 'js', 18977 'path': 'dragdrop/dragdrop-min.js', 18978 'requires': ['dom', 'event'] 18979 }, 18980 18981 'editor': { 18982 'type': 'js', 18983 'path': 'editor/editor-min.js', 18984 'requires': ['menu', 'element', 'button'], 18985 'optional': ['animation', 'dragdrop'], 18986 'supersedes': ['simpleeditor'], 18987 'skinnable': true 18988 }, 18989 18990 'element': { 18991 'type': 'js', 18992 'path': 'element/element-min.js', 18993 'requires': ['dom', 'event'], 18994 'optional': ['event-mouseenter', 'event-delegate'] 18995 }, 18996 18997 'element-delegate': { 18998 'type': 'js', 18999 'path': 'element-delegate/element-delegate-min.js', 19000 'requires': ['element'] 19001 }, 19002 19003 'event': { 19004 'type': 'js', 19005 'path': 'event/event-min.js', 19006 'requires': ['yahoo'] 19007 }, 19008 19009 'event-simulate': { 19010 'type': 'js', 19011 'path': 'event-simulate/event-simulate-min.js', 19012 'requires': ['event'] 19013 }, 19014 19015 'event-delegate': { 19016 'type': 'js', 19017 'path': 'event-delegate/event-delegate-min.js', 19018 'requires': ['event'], 19019 'optional': ['selector'] 19020 }, 19021 19022 'event-mouseenter': { 19023 'type': 'js', 19024 'path': 'event-mouseenter/event-mouseenter-min.js', 19025 'requires': ['dom', 'event'] 19026 }, 19027 19028 'fonts': { 19029 'type': 'css', 19030 'path': 'fonts/fonts-min.css' 19031 }, 19032 19033 'get': { 19034 'type': 'js', 19035 'path': 'get/get-min.js', 19036 'requires': ['yahoo'] 19037 }, 19038 19039 'grids': { 19040 'type': 'css', 19041 'path': 'grids/grids-min.css', 19042 'requires': ['fonts'], 19043 'optional': ['reset'] 19044 }, 19045 19046 'history': { 19047 'type': 'js', 19048 'path': 'history/history-min.js', 19049 'requires': ['event'] 19050 }, 19051 19052 'imagecropper': { 19053 'type': 'js', 19054 'path': 'imagecropper/imagecropper-min.js', 19055 'requires': ['dragdrop', 'element', 'resize'], 19056 'skinnable': true 19057 }, 19058 19059 'imageloader': { 19060 'type': 'js', 19061 'path': 'imageloader/imageloader-min.js', 19062 'requires': ['event', 'dom'] 19063 }, 19064 19065 'json': { 19066 'type': 'js', 19067 'path': 'json/json-min.js', 19068 'requires': ['yahoo'] 19069 }, 19070 19071 'layout': { 19072 'type': 'js', 19073 'path': 'layout/layout-min.js', 19074 'requires': ['element'], 19075 'optional': ['animation', 'dragdrop', 'resize', 'selector'], 19076 'skinnable': true 19077 }, 19078 19079 'logger': { 19080 'type': 'js', 19081 'path': 'logger/logger-min.js', 19082 'requires': ['event', 'dom'], 19083 'optional': ['dragdrop'], 19084 'skinnable': true 19085 }, 19086 19087 'menu': { 19088 'type': 'js', 19089 'path': 'menu/menu-min.js', 19090 'requires': ['containercore'], 19091 'skinnable': true 19092 }, 19093 19094 'paginator': { 19095 'type': 'js', 19096 'path': 'paginator/paginator-min.js', 19097 'requires': ['element'], 19098 'skinnable': true 19099 }, 19100 19101 'profiler': { 19102 'type': 'js', 19103 'path': 'profiler/profiler-min.js', 19104 'requires': ['yahoo'] 19105 }, 19106 19107 19108 'profilerviewer': { 19109 'type': 'js', 19110 'path': 'profilerviewer/profilerviewer-min.js', 19111 'requires': ['profiler', 'yuiloader', 'element'], 19112 'skinnable': true 19113 }, 19114 19115 'progressbar': { 19116 'type': 'js', 19117 'path': 'progressbar/progressbar-min.js', 19118 'requires': ['element'], 19119 'optional': ['animation'], 19120 'skinnable': true 19121 }, 19122 19123 'reset': { 19124 'type': 'css', 19125 'path': 'reset/reset-min.css' 19126 }, 19127 19128 'reset-fonts-grids': { 19129 'type': 'css', 19130 'path': 'reset-fonts-grids/reset-fonts-grids.css', 19131 'supersedes': ['reset', 'fonts', 'grids', 'reset-fonts'], 19132 'rollup': 4 19133 }, 19134 19135 'reset-fonts': { 19136 'type': 'css', 19137 'path': 'reset-fonts/reset-fonts.css', 19138 'supersedes': ['reset', 'fonts'], 19139 'rollup': 2 19140 }, 19141 19142 'resize': { 19143 'type': 'js', 19144 'path': 'resize/resize-min.js', 19145 'requires': ['dragdrop', 'element'], 19146 'optional': ['animation'], 19147 'skinnable': true 19148 }, 19149 19150 'selector': { 19151 'type': 'js', 19152 'path': 'selector/selector-min.js', 19153 'requires': ['yahoo', 'dom'] 19154 }, 19155 19156 'simpleeditor': { 19157 'type': 'js', 19158 'path': 'editor/simpleeditor-min.js', 19159 'requires': ['element'], 19160 'optional': ['containercore', 'menu', 'button', 'animation', 'dragdrop'], 19161 'skinnable': true, 19162 'pkg': 'editor' 19163 }, 19164 19165 'slider': { 19166 'type': 'js', 19167 'path': 'slider/slider-min.js', 19168 'requires': ['dragdrop'], 19169 'optional': ['animation'], 19170 'skinnable': true 19171 }, 19172 19173 'storage': { 19174 'type': 'js', 19175 'path': 'storage/storage-min.js', 19176 'requires': ['yahoo', 'event', 'cookie'], 19177 'optional': ['swfstore'] 19178 }, 19179 19180 'stylesheet': { 19181 'type': 'js', 19182 'path': 'stylesheet/stylesheet-min.js', 19183 'requires': ['yahoo'] 19184 }, 19185 19186 'swf': { 19187 'type': 'js', 19188 'path': 'swf/swf-min.js', 19189 'requires': ['element'], 19190 'supersedes': ['swfdetect'] 19191 }, 19192 19193 'swfdetect': { 19194 'type': 'js', 19195 'path': 'swfdetect/swfdetect-min.js', 19196 'requires': ['yahoo'] 19197 }, 19198 19199 'swfstore': { 19200 'type': 'js', 19201 'path': 'swfstore/swfstore-min.js', 19202 'requires': ['element', 'cookie', 'swf'] 19203 }, 19204 19205 'tabview': { 19206 'type': 'js', 19207 'path': 'tabview/tabview-min.js', 19208 'requires': ['element'], 19209 'optional': ['connection'], 19210 'skinnable': true 19211 }, 19212 19213 'treeview': { 19214 'type': 'js', 19215 'path': 'treeview/treeview-min.js', 19216 'requires': ['event', 'dom'], 19217 'optional': ['json', 'animation', 'calendar'], 19218 'skinnable': true 19219 }, 19220 19221 'uploader': { 19222 'type': 'js', 19223 'path': 'uploader/uploader-min.js', 19224 'requires': ['element'] 19225 }, 19226 19227 'utilities': { 19228 'type': 'js', 19229 'path': 'utilities/utilities.js', 19230 'supersedes': ['yahoo', 'event', 'dragdrop', 'animation', 'dom', 'connection', 'element', 'yahoo-dom-event', 'get', 'yuiloader', 'yuiloader-dom-event'], 19231 'rollup': 8 19232 }, 19233 19234 'yahoo': { 19235 'type': 'js', 19236 'path': 'yahoo/yahoo-min.js' 19237 }, 19238 19239 'yahoo-dom-event': { 19240 'type': 'js', 19241 'path': 'yahoo-dom-event/yahoo-dom-event.js', 19242 'supersedes': ['yahoo', 'event', 'dom'], 19243 'rollup': 3 19244 }, 19245 19246 'yuiloader': { 19247 'type': 'js', 19248 'path': 'yuiloader/yuiloader-min.js', 19249 'supersedes': ['yahoo', 'get'] 19250 }, 19251 19252 'yuiloader-dom-event': { 19253 'type': 'js', 19254 'path': 'yuiloader-dom-event/yuiloader-dom-event.js', 19255 'supersedes': ['yahoo', 'dom', 'event', 'get', 'yuiloader', 'yahoo-dom-event'], 19256 'rollup': 5 19257 }, 19258 19259 'yuitest': { 19260 'type': 'js', 19261 'path': 'yuitest/yuitest-min.js', 19262 'requires': ['logger'], 19263 'optional': ['event-simulate'], 19264 'skinnable': true 19265 } 19266 } 19267}, 19268 ObjectUtil: { 19269 appendArray: function(o, a) { 19270 if (a) { 19271 for (var i=0; i<a.length; i=i+1) { 19272 o[a[i]] = true; 19273 } 19274 } 19275 }, 19276 19277 keys: function(o, ordered) { 19278 var a=[], i; 19279 for (i in o) { 19280 if (lang.hasOwnProperty(o, i)) { 19281 a.push(i); 19282 } 19283 } 19284 19285 return a; 19286 } 19287 }, 19288 19289 ArrayUtil: { 19290 19291 appendArray: function(a1, a2) { 19292 Array.prototype.push.apply(a1, a2); 19293 /* 19294 for (var i=0; i<a2.length; i=i+1) { 19295 a1.push(a2[i]); 19296 } 19297 */ 19298 }, 19299 19300 indexOf: function(a, val) { 19301 for (var i=0; i<a.length; i=i+1) { 19302 if (a[i] === val) { 19303 return i; 19304 } 19305 } 19306 19307 return -1; 19308 }, 19309 19310 toObject: function(a) { 19311 var o = {}; 19312 for (var i=0; i<a.length; i=i+1) { 19313 o[a[i]] = true; 19314 } 19315 19316 return o; 19317 }, 19318 19319 /* 19320 * Returns a unique array. Does not maintain order, which is fine 19321 * for this application, and performs better than it would if it 19322 * did. 19323 */ 19324 uniq: function(a) { 19325 return YUI.ObjectUtil.keys(YUI.ArrayUtil.toObject(a)); 19326 } 19327 } 19328 }; 19329 19330 YAHOO.util.YUILoader = function(o) { 19331 19332 /**
19333 * Internal callback to handle multiple internal insert() calls 19334 * so that css is inserted prior to js 19335 * @property _internalCallback 19336 * @private 19337 */ 19338 this._internalCallback = null; 19339 19340 /** 19341 * Use the YAHOO environment listener to detect script load. This 19342 * is only switched on for Safari 2.x and below. 19343 * @property _useYahooListener 19344 * @private 19345 */ 19346 this._useYahooListener = false; 19347 19348 /** 19349 * Callback that will be executed when the loader is finished 19350 * with an insert 19351 * @method onSuccess 19352 * @type function 19353 */ 19354 this.onSuccess = null; 19355 19356 /** 19357 * Callback that will be executed if there is a failure 19358 * @method onFailure 19359 * @type function 19360 */ 19361 this.onFailure = Y.log; 19362 19363 /** 19364 * Callback that will be executed each time a new module is loaded 19365 * @method onProgress 19366 * @type function 19367 */ 19368 this.onProgress = null; 19369 19370 /** 19371 * Callback that will be executed if a timeout occurs 19372 * @method onTimeout 19373 * @type function 19374 */ 19375 this.onTimeout = null; 19376 19377 /** 19378 * The execution scope for all callbacks 19379 * @property scope 19380 * @default this 19381 */ 19382 this.scope = this; 19383 19384 /** 19385 * Data that is passed to all callbacks 19386 * @property data 19387 */ 19388 this.data = null; 19389 19390 /** 19391 * Node reference or id where new nodes should be inserted before 19392 * @property insertBefore 19393 * @type string|HTMLElement 19394 */ 19395 this.insertBefore = null; 19396 19397 /** 19398 * The charset attribute for inserted nodes 19399 * @property charset 19400 * @type string 19401 * @default utf-8 19402 */ 19403 this.charset = null; 19404 19405 /** 19406 * The name of the variable in a sandbox or script node 19407 * (for external script support in Safari 2.x and earlier) 19408 * to reference when the load is complete. If this variable 19409 * is not available in the specified scripts, the operation will 19410 * fail. 19411 * @property varName 19412 * @type string 19413 */ 19414 this.varName = null; 19415 19416 /** 19417 * The base directory. 19418 * @property base 19419 * @type string 19420 * @default http://yui.yahooapis.com/[YUI VERSION]/build/ 19421 */ 19422 this.base = YUI.info.base; 19423 19424 /** 19425 * Base path for the combo service 19426 * @property comboBase 19427 * @type string 19428 * @default http://yui.yahooapis.com/combo? 19429 */ 19430 this.comboBase = YUI.info.comboBase; 19431 19432 /** 19433 * If configured, YUI will use the the combo handler on the 19434 * Yahoo! CDN to pontentially reduce the number of http requests 19435 * required. 19436 * @property combine 19437 * @type boolean 19438 * @default false 19439 */ 19440 // this.combine = (o && !('base' in o)); 19441 this.combine = false; 19442 19443 19444 /** 19445 * Root path to prepend to module path for the combo 19446 * service 19447 * @property root 19448 * @type string 19449 * @default [YUI VERSION]/build/ 19450 */ 19451 this.root = YUI.info.root; 19452 19453 /** 19454 * Timeout value in milliseconds. If set, this value will be used by 19455 * the get utility. the timeout event will fire if 19456 * a timeout occurs. 19457 * @property timeout 19458 * @type int 19459 */ 19460 this.timeout = 0; 19461 19462 /** 19463 * A list of modules that should not be loaded, even if 19464 * they turn up in the dependency tree 19465 * @property ignore 19466 * @type string[] 19467 */ 19468 this.ignore = null; 19469 19470 /** 19471 * A list of modules that should always be loaded, even 19472 * if they have already been inserted into the page. 19473 * @property force 19474 * @type string[] 19475 */ 19476 this.force = null; 19477 19478 /** 19479 * Should we allow rollups 19480 * @property allowRollup 19481 * @type boolean 19482 * @default true 19483 */ 19484 this.allowRollup = true; 19485 19486 /** 19487 * A filter to apply to result urls. This filter will modify the default 19488 * path for all modules. The default path for the YUI library is the 19489 * minified version of the files (e.g., event-min.js). The filter property 19490 * can be a predefined filter or a custom filter. The valid predefined 19491 * filters are: 19492 * <dl> 19493 * <dt>DEBUG</dt> 19494 * <dd>Selects the debug versions of the library (e.g., event-debug.js). 19495 * This option will automatically include the logger widget</dd> 19496 * <dt>RAW</dt> 19497 * <dd>Selects the non-minified version of the library (e.g., event.js). 19498 * </dl> 19499 * You can also define a custom filter, which must be an object literal 19500 * containing a search expression and a replace string: 19501 * <pre> 19502 * myFilter: { 19503 * 'searchExp': "-min\\.js", 19504 * 'replaceStr': "-debug.js" 19505 * } 19506 * </pre> 19507 * @property filter 19508 * @type string|{searchExp: string, replaceStr: string} 19509 */ 19510 this.filter = null; 19511 19512 /** 19513 * The list of requested modules 19514 * @property required 19515 * @type {string: boolean} 19516 */ 19517 this.required = {}; 19518 19519 /** 19520 * The library metadata 19521 * @property moduleInfo 19522 */ 19523 this.moduleInfo = lang.merge(YUI.info.moduleInfo); 19524 19525 /** 19526 * List of rollup files found in the library metadata 19527 * @property rollups 19528 */ 19529 this.rollups = null; 19530 19531 /** 19532 * Whether or not to load optional dependencies for 19533 * the requested modules 19534 * @property loadOptional 19535 * @type boolean 19536 * @default false 19537 */ 19538 this.loadOptional = false;
19539 19540 /** 19541 * All of the derived dependencies in sorted order, which 19542 * will be populated when either calculate() or insert() 19543 * is called 19544 * @property sorted 19545 * @type string[] 19546 */ 19547 this.sorted = []; 19548 19549 /** 19550 * Set when beginning to compute the dependency tree. 19551 * Composed of what YAHOO reports to be loaded combined 19552 * with what has been loaded by the tool 19553 * @propery loaded 19554 * @type {string: boolean} 19555 */ 19556 this.loaded = {}; 19557 19558 /** 19559 * Flag to indicate the dependency tree needs to be recomputed 19560 * if insert is called again. 19561 * @property dirty 19562 * @type boolean 19563 * @default true 19564 */ 19565 this.dirty = true; 19566 19567 /** 19568 * List of modules inserted by the utility 19569 * @property inserted 19570 * @type {string: boolean} 19571 */ 19572 this.inserted = {}; 19573 19574 /** 19575 * Provides the information used to skin the skinnable components. 19576 * The following skin definition would result in 'skin1' and 'skin2' 19577 * being loaded for calendar (if calendar was requested), and 19578 * 'sam' for all other skinnable components: 19579 * 19580 * <code> 19581 * skin: { 19582 * 19583 * // The default skin, which is automatically applied if not 19584 * // overriden by a component-specific skin definition. 19585 * // Change this in to apply a different skin globally 19586 * defaultSkin: 'sam', 19587 * 19588 * // This is combined with the loader base property to get 19589 * // the default root directory for a skin. ex: 19590 * // http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/ 19591 * base: 'assets/skins/', 19592 * 19593 * // The name of the rollup css file for the skin 19594 * path: 'skin.css', 19595 * 19596 * // The number of skinnable components requested that are 19597 * // required before using the rollup file rather than the 19598 * // individual component css files 19599 * rollup: 3, 19600 * 19601 * // Any component-specific overrides can be specified here, 19602 * // making it possible to load different skins for different 19603 * // components. It is possible to load more than one skin 19604 * // for a given component as well. 19605 * overrides: { 19606 * calendar: ['skin1', 'skin2'] 19607 * } 19608 * } 19609 * </code> 19610 * @property skin 19611 */ 19612 19613 var self = this; 19614 19615 env.listeners.push(function(m) { 19616 if (self._useYahooListener) { 19617 //Y.log("YAHOO listener: " + m.name); 19618 self.loadNext(m.name); 19619 } 19620 }); 19621 19622 this.skin = lang.merge(YUI.info.skin); 19623 19624 this._config(o); 19625 19626 }; 19627 19628 Y.util.YUILoader.prototype = { 19629 19630 FILTERS: { 19631 RAW: { 19632 'searchExp': "-min\\.js", 19633 'replaceStr': ".js" 19634 }, 19635 DEBUG: { 19636 'searchExp': "-min\\.js", 19637 'replaceStr': "-debug.js" 19638 } 19639 }, 19640 19641 SKIN_PREFIX: "skin-", 19642 19643 _config: function(o) { 19644 19645 // apply config values 19646 if (o) { 19647 for (var i in o) { 19648 if (lang.hasOwnProperty(o, i)) { 19649 if (i == "require") { 19650 this.require(o[i]); 19651 } else { 19652 this[i] = o[i]; 19653 } 19654 } 19655 } 19656 } 19657 19658 // fix filter 19659 var f = this.filter; 19660 19661 if (lang.isString(f)) { 19662 f = f.toUpperCase(); 19663 19664 // the logger must be available in order to use the debug 19665 // versions of the library 19666 if (f === "DEBUG") { 19667 this.require("logger"); 19668 } 19669 19670 // hack to handle a a bug where LogWriter is being instantiated 19671 // at load time, and the loader has no way to sort above it 19672 // at the moment. 19673 if (!Y.widget.LogWriter) { 19674 Y.widget.LogWriter = function() { 19675 return Y; 19676 }; 19677 } 19678 19679 this.filter = this.FILTERS[f]; 19680 } 19681 19682 }, 19683 19684 /** Add a new module to the component metadata. 19685 * <dl> 19686 * <dt>name:</dt> <dd>required, the component name</dd> 19687 * <dt>type:</dt> <dd>required, the component type (js or css)</dd> 19688 * <dt>path:</dt> <dd>required, the path to the script from "base"</dd> 19689 * <dt>requires:</dt> <dd>array of modules required by this component</dd> 19690 * <dt>optional:</dt> <dd>array of optional modules for this component</dd> 19691 * <dt>supersedes:</dt> <dd>array of the modules this component replaces</dd> 19692 * <dt>after:</dt> <dd>array of modules the components which, if present, should be sorted above this one</dd> 19693 * <dt>rollup:</dt> <dd>the number of superseded modules required for automatic rollup</dd> 19694 * <dt>fullpath:</dt> <dd>If fullpath is specified, this is used instead of the configured base + path</dd> 19695 * <dt>skinnable:</dt> <dd>flag to determine if skin assets should automatically be pulled in</dd> 19696 * </dl> 19697 * @method addModule
19698 * @param o An object containing the module data 19699 * @return {boolean} true if the module was added, false if 19700 * the object passed in did not provide all required attributes 19701 */ 19702 addModule: function(o) { 19703 19704 if (!o || !o.name || !o.type || (!o.path && !o.fullpath)) { 19705 return false; 19706 } 19707 19708 o.ext = ('ext' in o) ? o.ext : true; 19709 o.requires = o.requires || []; 19710 19711 this.moduleInfo[o.name] = o; 19712 this.dirty = true; 19713 19714 return true; 19715 }, 19716 19717 /** 19718 * Add a requirement for one or more module 19719 * @method require 19720 * @param what {string[] | string*} the modules to load 19721 */ 19722 require: function(what) { 19723 var a = (typeof what === "string") ? arguments : what; 19724 this.dirty = true; 19725 YUI.ObjectUtil.appendArray(this.required, a); 19726 }, 19727 19728 /** 19729 * Adds the skin def to the module info 19730 * @method _addSkin 19731 * @param skin {string} the name of the skin 19732 * @param mod {string} the name of the module 19733 * @return {string} the module name for the skin 19734 * @private 19735 */ 19736 _addSkin: function(skin, mod) { 19737 19738 // Add a module definition for the skin rollup css 19739 var name = this.formatSkin(skin), info = this.moduleInfo, 19740 sinf = this.skin, ext = info[mod] && info[mod].ext; 19741 19742 // Y.log('ext? ' + mod + ": " + ext); 19743 if (!info[name]) { 19744 // Y.log('adding skin ' + name); 19745 this.addModule({ 19746 'name': name, 19747 'type': 'css', 19748 'path': sinf.base + skin + '/' + sinf.path, 19749 //'supersedes': '*', 19750 'after': sinf.after, 19751 'rollup': sinf.rollup, 19752 'ext': ext 19753 }); 19754 } 19755 19756 // Add a module definition for the module-specific skin css 19757 if (mod) { 19758 name = this.formatSkin(skin, mod); 19759 if (!info[name]) { 19760 var mdef = info[mod], pkg = mdef.pkg || mod; 19761 // Y.log('adding skin ' + name); 19762 this.addModule({ 19763 'name': name, 19764 'type': 'css', 19765 'after': sinf.after, 19766 'path': pkg + '/' + sinf.base + skin + '/' + mod + '.css', 19767 'ext': ext 19768 }); 19769 } 19770 } 19771 19772 return name; 19773 }, 19774 19775 /**
19776 * Returns an object containing properties for all modules required 19777 * in order to load the requested module 19778 * @method getRequires 19779 * @param mod The module definition from moduleInfo 19780 */ 19781 getRequires: function(mod) { 19782 if (!mod) { 19783 return []; 19784 } 19785 19786 if (!this.dirty && mod.expanded) { 19787 return mod.expanded; 19788 } 19789 19790 mod.requires=mod.requires || []; 19791 var i, d=[], r=mod.requires, o=mod.optional, info=this.moduleInfo, m; 19792 for (i=0; i<r.length; i=i+1) { 19793 d.push(r[i]); 19794 m = info[r[i]]; 19795 YUI.ArrayUtil.appendArray(d, this.getRequires(m)); 19796 } 19797 19798 if (o && this.loadOptional) { 19799 for (i=0; i<o.length; i=i+1) { 19800 d.push(o[i]); 19801 YUI.ArrayUtil.appendArray(d, this.getRequires(info[o[i]])); 19802 } 19803 } 19804 19805 mod.expanded = YUI.ArrayUtil.uniq(d); 19806 19807 return mod.expanded; 19808 }, 19809 19810 19811 /** 19812 * Returns an object literal of the modules the supplied module satisfies 19813 * @method getProvides 19814 * @param name{string} The name of the module 19815 * @param notMe {string} don't add this module name, only include superseded modules 19816 * @return what this module provides 19817 */ 19818 getProvides: function(name, notMe) { 19819 var addMe = !(notMe), ckey = (addMe) ? PROV : SUPER, 19820 m = this.moduleInfo[name], o = {}; 19821 19822 if (!m) { 19823 return o; 19824 } 19825 19826 if (m[ckey]) { 19827// Y.log('cached: ' + name + ' ' + ckey + ' ' + lang.dump(this.moduleInfo[name][ckey], 0)); 19828 return m[ckey]; 19829 } 19830 19831 var s = m.supersedes, done={}, me = this; 19832 19833 // use worker to break cycles 19834 var add = function(mm) { 19835 if (!done[mm]) { 19836 // Y.log(name + ' provides worker trying: ' + mm); 19837 done[mm] = true; 19838 // we always want the return value normal behavior 19839 // (provides) for superseded modules. 19840 lang.augmentObject(o, me.getProvides(mm)); 19841 } 19842 19843 // else { 19844 // Y.log(name + ' provides worker skipping done: ' + mm); 19845 // } 19846 }; 19847 19848 // calculate superseded modules 19849 if (s) { 19850 for (var i=0; i<s.length; i=i+1) { 19851 add(s[i]); 19852 } 19853 } 19854 19855 // supersedes cache 19856 m[SUPER] = o; 19857 // provides cache 19858 m[PROV] = lang.merge(o); 19859 m[PROV][name] = true; 19860 19861// Y.log(name + " supersedes " + lang.dump(m[SUPER], 0)); 19862// Y.log(name + " provides " + lang.dump(m[PROV], 0)); 19863 19864 return m[ckey]; 19865 }, 19866 19867 19868 /** 19869 * Calculates the dependency tree, the result is stored in the sorted 19870 * property 19871 * @method calculate 19872 * @param o optional options object 19873 */ 19874 calculate: function(o) { 19875 if (o || this.dirty) { 19876 this._config(o); 19877 this._setup(); 19878 this._explode(); 19879 if (this.allowRollup) { 19880 this._rollup(); 19881 } 19882 this._reduce(); 19883 this._sort(); 19884 19885 // Y.log("after calculate: " + lang.dump(this.required)); 19886 19887 this.dirty = false; 19888 } 19889 }, 19890 19891 /** 19892 * Investigates the current YUI configuration on the page. By default, 19893 * modules already detected will not be loaded again unless a force 19894 * option is encountered. Called by calculate() 19895 * @method _setup 19896 * @private 19897 */ 19898 _setup: function() { 19899 19900 var info = this.moduleInfo, name, i, j; 19901 19902 // Create skin modules 19903 for (name in info) { 19904 19905 if (lang.hasOwnProperty(info, name)) { 19906 var m = info[name]; 19907 if (m && m.skinnable) { 19908 // Y.log("skinning: " + name); 19909 var o=this.skin.overrides, smod; 19910 if (o && o[name]) { 19911 for (i=0; i<o[name].length; i=i+1) { 19912 smod = this._addSkin(o[name][i], name); 19913 } 19914 } else { 19915 smod = this._addSkin(this.skin.defaultSkin, name); 19916 } 19917 19918 if (YUI.ArrayUtil.indexOf(m.requires, smod) == -1) { 19919 m.requires.push(smod); 19920 } 19921 } 19922 } 19923 19924 } 19925 19926 var l = lang.merge(this.inserted); // shallow clone 19927 19928 if (!this._sandbox) { 19929 l = lang.merge(l, env.modules); 19930 } 19931 19932 // Y.log("Already loaded stuff: " + lang.dump(l, 0)); 19933 19934 // add the ignore list to the list of loaded packages 19935 if (this.ignore) { 19936 YUI.ObjectUtil.appendArray(l, this.ignore); 19937 } 19938 19939 // remove modules on the force list from the loaded list 19940 if (this.force) { 19941 for (i=0; i<this.force.length; i=i+1) { 19942 if (this.force[i] in l) { 19943 delete l[this.force[i]]; 19944 } 19945 } 19946 } 19947 19948 // expand the list to include superseded modules 19949 for (j in l) { 19950 // Y.log("expanding: " + j); 19951 if (lang.hasOwnProperty(l, j)) { 19952 lang.augmentObject(l, this.getProvides(j)); 19953 } 19954 } 19955 19956 // Y.log("loaded expanded: " + lang.dump(l, 0)); 19957 19958 this.loaded = l; 19959 19960 }, 19961 19962 19963 /** 19964 * Inspects the required modules list looking for additional 19965 * dependencies. Expands the required list to include all 19966 * required modules. Called by calculate() 19967 * @method _explode 19968 * @private 19969 */ 19970 _explode: function() { 19971 19972 var r=this.required, i, mod; 19973 19974 for (i in r) { 19975 if (lang.hasOwnProperty(r, i)) { 19976 mod = this.moduleInfo[i]; 19977 if (mod) { 19978 19979 var req = this.getRequires(mod); 19980 19981 if (req) { 19982 YUI.ObjectUtil.appendArray(r, req); 19983 } 19984 } 19985 } 19986 } 19987 }, 19988 19989 /* 19990 * @method _skin 19991 * @private 19992 * @deprecated 19993 */ 19994 _skin: function() { 19995 }, 19996 19997 /** 19998 * Returns the skin module name for the specified skin name. If a 19999 * module name is supplied, the returned skin module name is 20000 * specific to the module passed in. 20001 * @method formatSkin
20002 * @param skin {string} the name of the skin 20003 * @param mod {string} optional: the name of a module to skin 20004 * @return {string} the full skin module name 20005 */ 20006 formatSkin: function(skin, mod) { 20007 var s = this.SKIN_PREFIX + skin; 20008 if (mod) { 20009 s = s + "-" + mod; 20010 } 20011 20012 return s; 20013 }, 20014 20015 /** 20016 * Reverses <code>formatSkin</code>, providing the skin name and 20017 * module name if the string matches the pattern for skins. 20018 * @method parseSkin 20019 * @param mod {string} the module name to parse 20020 * @return {skin: string, module: string} the parsed skin name 20021 * and module name, or null if the supplied string does not match 20022 * the skin pattern 20023 */ 20024 parseSkin: function(mod) { 20025 20026 if (mod.indexOf(this.SKIN_PREFIX) === 0) { 20027 var a = mod.split("-"); 20028 return {skin: a[1], module: a[2]}; 20029 } 20030 20031 return null; 20032 }, 20033 20034 /** 20035 * Look for rollup packages to determine if all of the modules a 20036 * rollup supersedes are required. If so, include the rollup to 20037 * help reduce the total number of connections required. Called 20038 * by calculate() 20039 * @method _rollup 20040 * @private 20041 */ 20042 _rollup: function() { 20043 var i, j, m, s, rollups={}, r=this.required, roll, 20044 info = this.moduleInfo; 20045 20046 // find and cache rollup modules 20047 if (this.dirty || !this.rollups) { 20048 for (i in info) { 20049 if (lang.hasOwnProperty(info, i)) { 20050 m = info[i]; 20051 //if (m && m.rollup && m.supersedes) { 20052 if (m && m.rollup) { 20053 rollups[i] = m; 20054 } 20055 } 20056 } 20057 20058 this.rollups = rollups; 20059 } 20060 20061 // make as many passes as needed to pick up rollup rollups 20062 for (;;) { 20063 var rolled = false; 20064 20065 // go through the rollup candidates 20066 for (i in rollups) { 20067 20068 // there can be only one 20069 if (!r[i] && !this.loaded[i]) { 20070 m =info[i]; s = m.supersedes; roll=false; 20071 20072 if (!m.rollup) { 20073 continue; 20074 } 20075 20076 var skin = (m.ext) ? false : this.parseSkin(i), c = 0; 20077 20078 // Y.log('skin? ' + i + ": " + skin); 20079 if (skin) { 20080 for (j in r) { 20081 if (lang.hasOwnProperty(r, j)) { 20082 if (i !== j && this.parseSkin(j)) { 20083 c++; 20084 roll = (c >= m.rollup); 20085 if (roll) { 20086 // Y.log("skin rollup " + lang.dump(r)); 20087 break; 20088 } 20089 } 20090 } 20091 } 20092 20093 } else { 20094 20095 // check the threshold 20096 for (j=0;j<s.length;j=j+1) { 20097 20098 // if the superseded module is loaded, we can't load the rollup 20099 if (this.loaded[s[j]] && (!YUI.dupsAllowed[s[j]])) { 20100 roll = false; 20101 break; 20102 // increment the counter if this module is required. if we are 20103 // beyond the rollup threshold, we will use the rollup module 20104 } else if (r[s[j]]) { 20105 c++; 20106 roll = (c >= m.rollup); 20107 if (roll) { 20108 // Y.log("over thresh " + c + ", " + lang.dump(r)); 20109 break; 20110 } 20111 } 20112 } 20113 } 20114 20115 if (roll) { 20116 // Y.log("rollup: " + i + ", " + lang.dump(this, 1)); 20117 // add the rollup 20118 r[i] = true; 20119 rolled = true; 20120 20121 // expand the rollup's dependencies 20122 this.getRequires(m); 20123 } 20124 } 20125 } 20126 20127 // if we made it here w/o rolling up something, we are done 20128 if (!rolled) { 20129 break; 20130 } 20131 } 20132 }, 20133 20134 /** 20135 * Remove superceded modules and loaded modules. Called by 20136 * calculate() after we have the mega list of all dependencies 20137 * @method _reduce 20138 * @private 20139 */ 20140 _reduce: function() { 20141 20142 var i, j, s, m, r=this.required; 20143 for (i in r) { 20144 20145 // remove if already loaded 20146 if (i in this.loaded) { 20147 delete r[i]; 20148 20149 // remove anything this module supersedes 20150 } else { 20151 20152 var skinDef = this.parseSkin(i); 20153 20154 if (skinDef) { 20155 //YAHOO.log("skin found in reduce: " + skinDef.skin + ", " + skinDef.module); 20156 // the skin rollup will not have a module name 20157 if (!skinDef.module) { 20158 var skin_pre = this.SKIN_PREFIX + skinDef.skin; 20159 //YAHOO.log("skin_pre: " + skin_pre); 20160 for (j in r) { 20161 20162 if (lang.hasOwnProperty(r, j)) { 20163 m = this.moduleInfo[j]; 20164 var ext = m && m.ext; 20165 if (!ext && j !== i && j.indexOf(skin_pre) > -1) {
20166 // Y.log ("removing component skin: " + j); 20167 delete r[j]; 20168 } 20169 } 20170 } 20171 } 20172 } else { 20173 20174 m = this.moduleInfo[i]; 20175 s = m && m.supersedes; 20176 if (s) { 20177 for (j=0; j<s.length; j=j+1) { 20178 if (s[j] in r) { 20179 delete r[s[j]]; 20180 } 20181 } 20182 } 20183 } 20184 } 20185 } 20186 }, 20187 20188 _onFailure: function(msg) { 20189 YAHOO.log('Failure', 'info', 'loader'); 20190 20191 var f = this.onFailure; 20192 if (f) { 20193 f.call(this.scope, { 20194 msg: 'failure: ' + msg, 20195 data: this.data, 20196 success: false 20197 }); 20198 } 20199 }, 20200 20201 _onTimeout: function() { 20202 YAHOO.log('Timeout', 'info', 'loader'); 20203 var f = this.onTimeout; 20204 if (f) { 20205 f.call(this.scope, { 20206 msg: 'timeout', 20207 data: this.data, 20208 success: false 20209 }); 20210 } 20211 }, 20212 20213 /** 20214 * Sorts the dependency tree. The last step of calculate() 20215 * @method _sort 20216 * @private 20217 */ 20218 _sort: function() { 20219 // create an indexed list 20220 var s=[], info=this.moduleInfo, loaded=this.loaded, 20221 checkOptional=!this.loadOptional, me = this; 20222 20223 // returns true if b is not loaded, and is required 20224 // directly or by means of modules it supersedes. 20225 var requires = function(aa, bb) { 20226 20227 var mm=info[aa]; 20228 20229 if (loaded[bb] || !mm) { 20230 return false; 20231 } 20232 20233 var ii, 20234 rr = mm.expanded, 20235 after = mm.after, 20236 other = info[bb], 20237 optional = mm.optional; 20238 20239 20240 // check if this module requires the other directly 20241 if (rr && YUI.ArrayUtil.indexOf(rr, bb) > -1) { 20242 return true; 20243 } 20244 20245 // check if this module should be sorted after the other 20246 if (after && YUI.ArrayUtil.indexOf(after, bb) > -1) { 20247 return true; 20248 } 20249 20250 // if loadOptional is not specified, optional dependencies still 20251 // must be sorted correctly when present. 20252 if (checkOptional && optional && YUI.ArrayUtil.indexOf(optional, bb) > -1) { 20253 return true; 20254 } 20255 20256 // check if this module requires one the other supersedes 20257 var ss=info[bb] && info[bb].supersedes; 20258 if (ss) { 20259 for (ii=0; ii<ss.length; ii=ii+1) { 20260 if (requires(aa, ss[ii])) { 20261 return true; 20262 } 20263 } 20264 } 20265 20266 // external css files should be sorted below yui css 20267 if (mm.ext && mm.type == 'css' && !other.ext && other.type == 'css') { 20268 return true; 20269 } 20270 20271 return false; 20272 }; 20273 20274 // get the required items out of the obj into an array so we 20275 // can sort 20276 for (var i in this.required) { 20277 if (lang.hasOwnProperty(this.required, i)) { 20278 s.push(i); 20279 } 20280 } 20281 20282 // pointer to the first unsorted item 20283 var p=0; 20284 20285 // keep going until we make a pass without moving anything 20286 for (;;) { 20287 20288 var l=s.length, a, b, j, k, moved=false; 20289 20290 // start the loop after items that are already sorted 20291 for (j=p; j<l; j=j+1) { 20292 20293 // check the next module on the list to see if its 20294 // dependencies have been met 20295 a = s[j]; 20296 20297 // check everything below current item and move if we 20298 // find a requirement for the current item 20299 for (k=j+1; k<l; k=k+1) { 20300 if (requires(a, s[k])) { 20301 20302 // extract the dependency so we can move it up 20303 b = s.splice(k, 1); 20304 20305 // insert the dependency above the item that 20306 // requires it 20307 s.splice(j, 0, b[0]); 20308 20309 moved = true; 20310 break; 20311 } 20312 } 20313 20314 // jump out of loop if we moved something 20315 if (moved) { 20316 break; 20317 // this item is sorted, move our pointer and keep going 20318 } else { 20319 p = p + 1; 20320 } 20321 } 20322 20323 // when we make it here and moved is false, we are 20324 // finished sorting 20325 if (!moved) { 20326 break; 20327 } 20328 20329 } 20330 20331 this.sorted = s; 20332 }, 20333 20334 toString: function() { 20335 var o = { 20336 type: "YUILoader", 20337 base: this.base, 20338 filter: this.filter, 20339 required: this.required, 20340 loaded: this.loaded, 20341 inserted: this.inserted 20342 }; 20343 20344 lang.dump(o, 1); 20345 }, 20346 20347 _combine: function() { 20348 20349 this._combining = []; 20350 20351 var self = this, 20352 s=this.sorted, 20353 len = s.length, 20354 js = this.comboBase, 20355 css = this.comboBase, 20356 target, 20357 startLen = js.length, 20358 i, m, type = this.loadType; 20359 20360 YAHOO.log('type ' + type); 20361 20362 for (i=0; i<len; i=i+1) { 20363 20364 m = this.moduleInfo[s[i]]; 20365 20366 if (m && !m.ext && (!type || type === m.type)) { 20367 20368 target = this.root + m.path; 20369 20370 // if (i < len-1) { 20371 target += '&'; 20372 // } 20373 20374 if (m.type == 'js') { 20375 js += target; 20376 } else { 20377 css += target; 20378 } 20379 20380 // YAHOO.log(target); 20381 this._combining.push(s[i]); 20382 } 20383 } 20384 20385 if (this._combining.length) { 20386 20387YAHOO.log('Attempting to combine: ' + this._combining, "info", "loader"); 20388 20389 var callback=function(o) { 20390 // YAHOO.log('Combo complete: ' + o.data, "info", "loader"); 20391 // this._combineComplete = true; 20392 20393 var c=this._combining, len=c.length, i, m; 20394 for (i=0; i<len; i=i+1) { 20395 this.inserted[c[i]] = true; 20396 } 20397 20398 this.loadNext(o.data); 20399 }, 20400 20401 loadScript = function() { 20402 // YAHOO.log('combining js: ' + js); 20403 if (js.length > startLen) { 20404 YAHOO.util.Get.script(self._filter(js), { 20405 data: self._loading, 20406 onSuccess: callback, 20407 onFailure: self._onFailure, 20408 onTimeout: self._onTimeout, 20409 insertBefore: self.insertBefore, 20410 charset: self.charset, 20411 timeout: self.timeout, 20412 scope: self 20413 }); 20414 } else { 20415 this.loadNext(); 20416 } 20417 }; 20418 20419 // load the css first 20420 // YAHOO.log('combining css: ' + css); 20421 if (css.length > startLen) { 20422 YAHOO.util.Get.css(this._filter(css), { 20423 data: this._loading, 20424 onSuccess: loadScript, 20425 onFailure: this._onFailure, 20426 onTimeout: this._onTimeout, 20427 insertBefore: this.insertBefore, 20428 charset: this.charset, 20429 timeout: this.timeout, 20430 scope: self 20431 }); 20432 } else { 20433 loadScript(); 20434 } 20435 20436 return; 20437 20438 } else { 20439 // this._combineComplete = true; 20440 this.loadNext(this._loading); 20441 } 20442 }, 20443 20444 /** 20445 * inserts the requested modules and their dependencies. 20446 * <code>type</code> can be "js" or "css". Both script and 20447 * css are inserted if type is not provided. 20448 * @method insert 20449 * @param o optional options object 20450 * @param type {string} the type of dependency to insert 20451 */ 20452 insert: function(o, type) { 20453 // if (o) { 20454 // Y.log("insert: " + lang.dump(o, 1) + ", " + type); 20455 // } else { 20456 // Y.log("insert: " + this.toString() + ", " + type); 20457 // } 20458 20459 // build the dependency list 20460 this.calculate(o); 20461 20462 20463 // set a flag to indicate the load has started 20464 this._loading = true; 20465 20466 // flag to indicate we are done with the combo service 20467 // and any additional files will need to be loaded 20468 // individually 20469 // this._combineComplete = false;
20470 20471 // keep the loadType (js, css or undefined) cached 20472 this.loadType = type; 20473 20474 if (this.combine) { 20475 return this._combine(); 20476 } 20477 20478 if (!type) { 20479 // Y.log("trying to load css first"); 20480 var self = this; 20481 this._internalCallback = function() { 20482 self._internalCallback = null; 20483 self.insert(null, "js"); 20484 }; 20485 this.insert(null, "css"); 20486 return; 20487 } 20488 20489 20490 // start the load 20491 this.loadNext(); 20492 20493 }, 20494 20495 /** 20496 * Interns the script for the requested modules. The callback is 20497 * provided a reference to the sandboxed YAHOO object. This only 20498 * applies to the script: css can not be sandboxed; css will be 20499 * loaded into the page normally if specified. 20500 * @method sandbox 20501 * @param callback {Function} the callback to exectued when the load is 20502 * complete. 20503 */ 20504 sandbox: function(o, type) { 20505 // if (o) { 20506 // YAHOO.log("sandbox: " + lang.dump(o, 1) + ", " + type); 20507 // } else { 20508 // YAHOO.log("sandbox: " + this.toString() + ", " + type); 20509 // } 20510 20511 var self = this, 20512 success = function(o) { 20513 20514 var idx=o.argument[0], name=o.argument[2]; 20515 20516 // store the response in the position it was requested 20517 self._scriptText[idx] = o.responseText; 20518 20519 // YAHOO.log("received: " + o.responseText.substr(0, 100) + ", " + idx); 20520 20521 if (self.onProgress) { 20522 self.onProgress.call(self.scope, { 20523 name: name, 20524 scriptText: o.responseText, 20525 xhrResponse: o, 20526 data: self.data 20527 }); 20528 } 20529 20530 // only generate the sandbox once everything is loaded 20531 self._loadCount++; 20532 20533 if (self._loadCount >= self._stopCount) { 20534 20535 // the variable to find 20536 var v = self.varName || "YAHOO"; 20537 20538 // wrap the contents of the requested modules in an anonymous function 20539 var t = "(function() {\n"; 20540 20541 // return the locally scoped reference. 20542 var b = "\nreturn " + v + ";\n})();"; 20543 20544 var ref = eval(t + self._scriptText.join("\n") + b); 20545 20546 self._pushEvents(ref); 20547 20548 if (ref) { 20549 self.onSuccess.call(self.scope, { 20550 reference: ref, 20551 data: self.data 20552 }); 20553 } else { 20554 self._onFailure.call(self.varName + " reference failure"); 20555 } 20556 } 20557 }, 20558 20559 failure = function(o) { 20560 self.onFailure.call(self.scope, { 20561 msg: "XHR failure", 20562 xhrResponse: o, 20563 data: self.data 20564 }); 20565 }; 20566 20567 self._config(o); 20568 20569 if (!self.onSuccess) { 20570throw new Error("You must supply an onSuccess handler for your sandbox"); 20571 } 20572 20573 self._sandbox = true; 20574 20575 20576 // take care of any css first (this can't be sandboxed) 20577 if (!type || type !== "js") { 20578 self._internalCallback = function() { 20579 self._internalCallback = null; 20580 self.sandbox(null, "js"); 20581 }; 20582 self.insert(null, "css"); 20583 return; 20584 } 20585 20586 // get the connection manager if not on the page 20587 if (!util.Connect) { 20588 // get a new loader instance to load connection. 20589 var ld = new YAHOO.util.YUILoader(); 20590 ld.insert({ 20591 base: self.base, 20592 filter: self.filter, 20593 require: "connection", 20594 insertBefore: self.insertBefore, 20595 charset: self.charset, 20596 onSuccess: function() { 20597 self.sandbox(null, "js"); 20598 }, 20599 scope: self 20600 }, "js"); 20601 return; 20602 } 20603 20604 self._scriptText = []; 20605 self._loadCount = 0; 20606 self._stopCount = self.sorted.length; 20607 self._xhr = []; 20608 20609 self.calculate(); 20610 20611 var s=self.sorted, l=s.length, i, m, url; 20612 20613 for (i=0; i<l; i=i+1) { 20614 m = self.moduleInfo[s[i]]; 20615 20616 // undefined modules cause a failure 20617 if (!m) { 20618 self._onFailure("undefined module " + m); 20619 for (var j=0;j<self._xhr.length;j=j+1) { 20620 self._xhr[j].abort(); 20621 } 20622 return; 20623 } 20624 20625 // css files should be done 20626 if (m.type !== "js") { 20627 self._loadCount++; 20628 continue; 20629 } 20630 20631 url = m.fullpath; 20632 url = (url) ? self._filter(url) : self._url(m.path); 20633 20634 // YAHOO.log("xhr request: " + url + ", " + i); 20635 20636 var xhrData = { 20637 success: success, 20638 failure: failure, 20639 scope: self, 20640 // [module index, module name, sandbox name] 20641 argument: [i, url, s[i]] 20642 }; 20643 20644 self._xhr.push(util.Connect.asyncRequest('GET', url, xhrData)); 20645 } 20646 }, 20647 20648 /** 20649 * Executed every time a module is loaded, and if we are in a load 20650 * cycle, we attempt to load the next script. Public so that it 20651 * is possible to call this if using a method other than 20652 * YAHOO.register to determine when scripts are fully loaded 20653 * @method loadNext 20654 * @param mname {string} optional the name of the module that has 20655 * been loaded (which is usually why it is time to load the next 20656 * one) 20657 */
20658 loadNext: function(mname) { 20659 20660 // It is possible that this function is executed due to something 20661 // else one the page loading a YUI module. Only react when we 20662 // are actively loading something 20663 if (!this._loading) { 20664 return; 20665 } 20666 20667 var self = this, 20668 donext = function(o) { 20669 self.loadNext(o.data); 20670 }, successfn, s = this.sorted, len=s.length, i, fn, m, url; 20671 20672 20673 if (mname) { 20674 20675 // if the module that was just loaded isn't what we were expecting, 20676 // continue to wait 20677 if (mname !== this._loading) { 20678 return; 20679 } 20680 20681 // YAHOO.log("loadNext executing, just loaded " + mname); 20682 20683 // The global handler that is called when each module is loaded 20684 // will pass that module name to this function. Storing this 20685 // data to avoid loading the same module multiple times 20686 this.inserted[mname] = true; 20687 20688 if (this.onProgress) { 20689 this.onProgress.call(this.scope, { 20690 name: mname, 20691 data: this.data 20692 }); 20693 } 20694 //var o = this.getProvides(mname); 20695 //this.inserted = lang.merge(this.inserted, o); 20696 } 20697 20698 20699 20700 for (i=0; i<len; i=i+1) { 20701 20702 // This.inserted keeps track of what the loader has loaded 20703 if (s[i] in this.inserted) { 20704 // YAHOO.log(s[i] + " alread loaded "); 20705 continue; 20706 } 20707 20708 // Because rollups will cause multiple load notifications 20709 // from YAHOO, loadNext may be called multiple times for 20710 // the same module when loading a rollup. We can safely 20711 // skip the subsequent requests 20712 if (s[i] === this._loading) { 20713 // YAHOO.log("still loading " + s[i] + ", waiting"); 20714 return; 20715 } 20716 20717 // log("inserting " + s[i]); 20718 m = this.moduleInfo[s[i]]; 20719 20720 if (!m) { 20721 this.onFailure.call(this.scope, { 20722 msg: "undefined module " + m, 20723 data: this.data 20724 }); 20725 return; 20726 } 20727 20728 // The load type is stored to offer the possibility to load 20729 // the css separately from the script. 20730 if (!this.loadType || this.loadType === m.type) { 20731 20732 successfn = donext; 20733 20734 this._loading = s[i]; 20735 //YAHOO.log("attempting to load " + s[i] + ", " + this.base); 20736 20737 fn = (m.type === "css") ? util.Get.css : util.Get.script; 20738 url = m.fullpath; 20739 url = (url) ? this._filter(url) : this._url(m.path); 20740 20741 // safari 2.x or lower, script, and part of YUI 20742 if (env.ua.webkit && env.ua.webkit < 420 && m.type === "js" && 20743 !m.varName) { 20744 //YUI.info.moduleInfo[s[i]]) { 20745 //YAHOO.log("using YAHOO env " + s[i] + ", " + m.varName); 20746 successfn = null; 20747 this._useYahooListener = true; 20748 } 20749 20750 fn(url, { 20751 data: s[i], 20752 onSuccess: successfn, 20753 onFailure: this._onFailure, 20754 onTimeout: this._onTimeout, 20755 insertBefore: this.insertBefore, 20756 charset: this.charset, 20757 timeout: this.timeout, 20758 varName: m.varName, 20759 scope: self 20760 }); 20761 20762 return; 20763 } 20764 } 20765 20766 // we are finished 20767 this._loading = null; 20768
20769 // internal callback for loading css first 20770 if (this._internalCallback) { 20771 var f = this._internalCallback; 20772 this._internalCallback = null; 20773 f.call(this); 20774 } else if (this.onSuccess) { 20775 this._pushEvents(); 20776 this.onSuccess.call(this.scope, { 20777 data: this.data 20778 }); 20779 } 20780 20781 }, 20782 20783 /** 20784 * In IE, the onAvailable/onDOMReady events need help when Event is 20785 * loaded dynamically 20786 * @method _pushEvents 20787 * @param {Function} optional function reference 20788 * @private 20789 */ 20790 _pushEvents: function(ref) { 20791 var r = ref || YAHOO; 20792 if (r.util && r.util.Event) { 20793 r.util.Event._load(); 20794 } 20795 }, 20796 20797 /** 20798 * Applies filter 20799 * method _filter 20800 * @return {string} the filtered string 20801 * @private 20802 */ 20803 _filter: function(str) { 20804 var f = this.filter; 20805 return (f) ? str.replace(new RegExp(f.searchExp, 'g'), f.replaceStr) : str; 20806 }, 20807 20808 /** 20809 * Generates the full url for a module 20810 * method _url 20811 * @param path {string} the path fragment 20812 * @return {string} the full url 20813 * @private 20814 */ 20815 _url: function(path) { 20816 return this._filter((this.base || "") + path); 20817 } 20818 20819 }; 20820 20821})(); 20822 20823YAHOO.register("yuiloader", YAHOO.util.YUILoader, {version: "2.9.0", build: "2800"}); 20824/* 20825Copyright (c) 2011, Yahoo! Inc. All rights reserved. 20826Code licensed under the BSD License: 20827http://developer.yahoo.com/yui/license.html 20828version: 2.9.0 20829*/ 20830/** 20831* @module button 20832* @description <p>The Button Control enables the creation of rich, graphical 20833* buttons that function like traditional HTML form buttons. <em>Unlike</em> 20834* traditional HTML form buttons, buttons created with the Button Control can have 20835* a label that is different from its value. With the inclusion of the optional 20836* <a href="module_menu.html">Menu Control</a>, the Button Control can also be 20837* used to create menu buttons and split buttons, controls that are not 20838* available natively in HTML. The Button Control can also be thought of as a 20839* way to create more visually engaging implementations of the browser's 20840* default radio-button and check-box controls.</p> 20841* <p>The Button Control supports the following types:</p> 20842* <dl> 20843* <dt>push</dt> 20844* <dd>Basic push button that can execute a user-specified command when 20845* pressed.</dd> 20846* <dt>link</dt> 20847* <dd>Navigates to a specified url when pressed.</dd> 20848* <dt>submit</dt> 20849* <dd>Submits the parent form when pressed.</dd> 20850* <dt>reset</dt> 20851* <dd>Resets the parent form when pressed.</dd> 20852* <dt>checkbox</dt> 20853* <dd>Maintains a "checked" state that can be toggled on and off.</dd> 20854* <dt>radio</dt> 20855* <dd>Maintains a "checked" state that can be toggled on and off. Use with 20856* the ButtonGroup class to create a set of controls that are mutually 20857* exclusive; checking one button in the set will uncheck all others in 20858* the group.</dd> 20859* <dt>menu</dt> 20860* <dd>When pressed will show/hide a menu.</dd> 20861* <dt>split</dt> 20862* <dd>Can execute a user-specified command or display a menu when pressed.</dd> 20863* </dl> 20864* @title Button 20865* @namespace YAHOO.widget 20866* @requires yahoo, dom, element, event 20867* @optional container, menu 20868*/ 20869 20870 20871(function () { 20872 20873 20874 /** 20875 * The Button class creates a rich, graphical button. 20876 * @param {String} p_oElement String specifying the id attribute of the 20877 * <code><input></code>, <code><button></code>, 20878 * <code><a></code>, or <code><span></code> element to 20879 * be used to create the button. 20880 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 20881 * one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http://www.w3.org 20882 * /TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-34812697"> 20883 * HTMLButtonElement</a>|<a href=" 20884 * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html# 20885 * ID-33759296">HTMLElement</a>} p_oElement Object reference for the 20886 * <code><input></code>, <code><button></code>, 20887 * <code><a></code>, or <code><span></code> element to be 20888 * used to create the button. 20889 * @param {Object} p_oElement Object literal specifying a set of 20890 * configuration attributes used to create the button. 20891 * @param {Object} p_oAttributes Optional. Object literal specifying a set 20892 * of configuration attributes used to create the button. 20893 * @namespace YAHOO.widget 20894 * @class Button 20895 * @constructor 20896 * @extends YAHOO.util.Element 20897 */ 20898 20899 20900 20901 // Shorthard for utilities 20902 20903 var Dom = YAHOO.util.Dom, 20904 Event = YAHOO.util.Event, 20905 Lang = YAHOO.lang, 20906 UA = YAHOO.env.ua, 20907 Overlay = YAHOO.widget.Overlay,
20908 Menu = YAHOO.widget.Menu, 20909 20910 20911 // Private member variables 20912 20913 m_oButtons = {}, // Collection of all Button instances 20914 m_oOverlayManager = null, // YAHOO.widget.OverlayManager instance 20915 m_oSubmitTrigger = null, // The button that submitted the form 20916 m_oFocusedButton = null; // The button that has focus 20917 20918 20919 20920 // Private methods 20921 20922 20923 20924 /** 20925 * @method createInputElement 20926 * @description Creates an <code><input></code> element of the 20927 * specified type. 20928 * @private 20929 * @param {String} p_sType String specifying the type of 20930 * <code><input></code> element to create. 20931 * @param {String} p_sName String specifying the name of 20932 * <code><input></code> element to create. 20933 * @param {String} p_sValue String specifying the value of 20934 * <code><input></code> element to create. 20935 * @param {String} p_bChecked Boolean specifying if the 20936 * <code><input></code> element is to be checked. 20937 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 20938 * one-html.html#ID-6043025">HTMLInputElement</a>} 20939 */ 20940 function createInputElement(p_sType, p_sName, p_sValue, p_bChecked) { 20941 20942 var oInput, 20943 sInput; 20944 20945 if (Lang.isString(p_sType) && Lang.isString(p_sName)) { 20946 20947 if (UA.ie && (UA.ie < 9)) { 20948 20949 /* 20950 For IE it is necessary to create the element with the 20951 "type," "name," "value," and "checked" properties set all 20952 at once. 20953 */ 20954 20955 sInput = "<input type=\"" + p_sType + "\" name=\"" + 20956 p_sName + "\""; 20957 20958 if (p_bChecked) { 20959 20960 sInput += " checked"; 20961 20962 } 20963 20964 sInput += ">"; 20965 20966 oInput = document.createElement(sInput); 20967 20968 oInput.value = p_sValue; 20969 20970 } else { 20971 20972 oInput = document.createElement("input"); 20973 oInput.name = p_sName; 20974 oInput.type = p_sType; 20975 oInput.value = p_sValue; 20976 20977 if (p_bChecked) { 20978 20979 oInput.checked = true; 20980 20981 } 20982 20983 } 20984 20985 20986 } 20987 20988 return oInput; 20989 20990 } 20991 20992 20993 /** 20994 * @method setAttributesFromSrcElement 20995 * @description Gets the values for all the attributes of the source element 20996 * (either <code><input></code> or <code><a></code>) that 20997 * map to Button configuration attributes and sets them into a collection 20998 * that is passed to the Button constructor. 20999 * @private 21000 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 21001 * one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http://www.w3.org/ 21002 * TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID- 21003 * 48250443">HTMLAnchorElement</a>} p_oElement Object reference to the HTML 21004 * element (either <code><input></code> or <code><span> 21005 * </code>) used to create the button. 21006 * @param {Object} p_oAttributes Object reference for the collection of 21007 * configuration attributes used to create the button. 21008 */ 21009 function setAttributesFromSrcElement(p_oElement, p_oAttributes) { 21010 21011 var sSrcElementNodeName = p_oElement.nodeName.toUpperCase(), 21012 sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME), 21013 me = this, 21014 oAttribute, 21015 oRootNode, 21016 sText; 21017 21018 21019 /** 21020 * @method setAttributeFromDOMAttribute 21021 * @description Gets the value of the specified DOM attribute and sets it 21022 * into the collection of configuration attributes used to configure 21023 * the button. 21024 * @private 21025 * @param {String} p_sAttribute String representing the name of the 21026 * attribute to retrieve from the DOM element. 21027 */ 21028 function setAttributeFromDOMAttribute(p_sAttribute) { 21029 21030 if (!(p_sAttribute in p_oAttributes)) { 21031 21032 /* 21033 Need to use "getAttributeNode" instead of "getAttribute" 21034 because using "getAttribute," IE will return the innerText 21035 of a <code><button></code> for the value attribute 21036 rather than the value of the "value" attribute. 21037 */ 21038 21039 oAttribute = p_oElement.getAttributeNode(p_sAttribute); 21040 21041 21042 if (oAttribute && ("value" in oAttribute)) { 21043 21044 YAHOO.log("Setting attribute \"" + p_sAttribute + 21045 "\" using source element's attribute value of \"" + 21046 oAttribute.value + "\"", "info", me.toString()); 21047 21048 p_oAttributes[p_sAttribute] = oAttribute.value; 21049 21050 } 21051 21052 } 21053 21054 } 21055 21056 21057 /** 21058 * @method setFormElementProperties
21059 * @description Gets the value of the attributes from the form element 21060 * and sets them into the collection of configuration attributes used to 21061 * configure the button. 21062 * @private 21063 */ 21064 function setFormElementProperties() { 21065 21066 setAttributeFromDOMAttribute("type"); 21067 21068 if (p_oAttributes.type == "button") { 21069 21070 p_oAttributes.type = "push"; 21071 21072 } 21073 21074 if (!("disabled" in p_oAttributes)) { 21075 21076 p_oAttributes.disabled = p_oElement.disabled; 21077 21078 } 21079 21080 setAttributeFromDOMAttribute("name"); 21081 setAttributeFromDOMAttribute("value"); 21082 setAttributeFromDOMAttribute("title"); 21083 21084 } 21085 21086 21087 switch (sSrcElementNodeName) { 21088 21089 case "A": 21090 21091 p_oAttributes.type = "link"; 21092 21093 setAttributeFromDOMAttribute("href"); 21094 setAttributeFromDOMAttribute("target"); 21095 21096 break; 21097 21098 case "INPUT": 21099 21100 setFormElementProperties(); 21101 21102 if (!("checked" in p_oAttributes)) { 21103 21104 p_oAttributes.checked = p_oElement.checked; 21105 21106 } 21107 21108 break; 21109 21110 case "BUTTON": 21111 21112 setFormElementProperties(); 21113 21114 oRootNode = p_oElement.parentNode.parentNode; 21115 21116 if (Dom.hasClass(oRootNode, sClass + "-checked")) { 21117 21118 p_oAttributes.checked = true; 21119 21120 } 21121 21122 if (Dom.hasClass(oRootNode, sClass + "-disabled")) { 21123 21124 p_oAttributes.disabled = true; 21125 21126 } 21127 21128 p_oElement.removeAttribute("value"); 21129 21130 p_oElement.setAttribute("type", "button"); 21131 21132 break; 21133 21134 } 21135 21136 p_oElement.removeAttribute("id"); 21137 p_oElement.removeAttribute("name"); 21138 21139 if (!("tabindex" in p_oAttributes)) { 21140 21141 p_oAttributes.tabindex = p_oElement.tabIndex; 21142 21143 } 21144 21145 if (!("label" in p_oAttributes)) { 21146 21147 // Set the "label" property 21148 21149 sText = sSrcElementNodeName == "INPUT" ? 21150 p_oElement.value : p_oElement.innerHTML; 21151 21152 21153 if (sText && sText.length > 0) { 21154 21155 p_oAttributes.label = sText; 21156 21157 } 21158 21159 } 21160 21161 } 21162 21163 21164 /** 21165 * @method initConfig 21166 * @description Initializes the set of configuration attributes that are 21167 * used to instantiate the button. 21168 * @private 21169 * @param {Object} Object representing the button's set of 21170 * configuration attributes. 21171 */ 21172 function initConfig(p_oConfig) { 21173 21174 var oAttributes = p_oConfig.attributes, 21175 oSrcElement = oAttributes.srcelement, 21176 sSrcElementNodeName = oSrcElement.nodeName.toUpperCase(), 21177 me = this; 21178 21179 21180 if (sSrcElementNodeName == this.NODE_NAME) { 21181 21182 p_oConfig.element = oSrcElement; 21183 p_oConfig.id = oSrcElement.id; 21184 21185 Dom.getElementsBy(function (p_oElement) { 21186 21187 switch (p_oElement.nodeName.toUpperCase()) { 21188 21189 case "BUTTON": 21190 case "A": 21191 case "INPUT": 21192 21193 setAttributesFromSrcElement.call(me, p_oElement, 21194 oAttributes); 21195 21196 break; 21197 21198 } 21199 21200 }, "*", oSrcElement); 21201 21202 } 21203 else { 21204 21205 switch (sSrcElementNodeName) { 21206 21207 case "BUTTON": 21208 case "A": 21209 case "INPUT": 21210 21211 setAttributesFromSrcElement.call(this, oSrcElement, 21212 oAttributes); 21213 21214 break; 21215 21216 } 21217 21218 } 21219 21220 } 21221 21222 21223 21224 // Constructor 21225 21226 YAHOO.widget.Button = function (p_oElement, p_oAttributes) { 21227 21228 if (!Overlay && YAHOO.widget.Overlay) { 21229 21230 Overlay = YAHOO.widget.Overlay; 21231 21232 } 21233 21234
21235 if (!Menu && YAHOO.widget.Menu) { 21236 21237 Menu = YAHOO.widget.Menu; 21238 21239 } 21240 21241 21242 var fnSuperClass = YAHOO.widget.Button.superclass.constructor, 21243 oConfig, 21244 oElement; 21245 21246 21247 if (arguments.length == 1 && !Lang.isString(p_oElement) && !p_oElement.nodeName) { 21248 21249 if (!p_oElement.id) { 21250 21251 p_oElement.id = Dom.generateId(); 21252 21253 YAHOO.log("No value specified for the button's \"id\" " + 21254 "attribute. Setting button id to \"" + p_oElement.id + 21255 "\".", "info", this.toString()); 21256 21257 } 21258 21259 YAHOO.log("No source HTML element. Building the button " + 21260 "using the set of configuration attributes.", "info", this.toString()); 21261 21262 fnSuperClass.call(this, (this.createButtonElement(p_oElement.type)), p_oElement); 21263 21264 } 21265 else { 21266 21267 oConfig = { element: null, attributes: (p_oAttributes || {}) }; 21268 21269 21270 if (Lang.isString(p_oElement)) { 21271 21272 oElement = Dom.get(p_oElement); 21273 21274 if (oElement) { 21275 21276 if (!oConfig.attributes.id) { 21277 21278 oConfig.attributes.id = p_oElement; 21279 21280 } 21281 21282 YAHOO.log("Building the button using an existing " + 21283 "HTML element as a source element.", "info", this.toString()); 21284 21285 21286 oConfig.attributes.srcelement = oElement; 21287 21288 initConfig.call(this, oConfig); 21289 21290 21291 if (!oConfig.element) { 21292 21293 YAHOO.log("Source element could not be used " + 21294 "as is. Creating a new HTML element for " + 21295 "the button.", "info", this.toString()); 21296 21297 oConfig.element = this.createButtonElement(oConfig.attributes.type); 21298 21299 } 21300 21301 fnSuperClass.call(this, oConfig.element, oConfig.attributes); 21302 21303 } 21304 21305 } 21306 else if (p_oElement.nodeName) { 21307 21308 if (!oConfig.attributes.id) { 21309 21310 if (p_oElement.id) { 21311 21312 oConfig.attributes.id = p_oElement.id; 21313 21314 } 21315 else { 21316 21317 oConfig.attributes.id = Dom.generateId(); 21318 21319 YAHOO.log("No value specified for the button's " + 21320 "\"id\" attribute. Setting button id to \"" + 21321 oConfig.attributes.id + "\".", "info", this.toString()); 21322 21323 } 21324 21325 } 21326 21327 YAHOO.log("Building the button using an existing HTML " + 21328 "element as a source element.", "info", this.toString()); 21329 21330 21331 oConfig.attributes.srcelement = p_oElement; 21332 21333 initConfig.call(this, oConfig); 21334 21335 21336 if (!oConfig.element) { 21337 21338 YAHOO.log("Source element could not be used as is." + 21339 " Creating a new HTML element for the button.", 21340 "info", this.toString()); 21341 21342 oConfig.element = this.createButtonElement(oConfig.attributes.type); 21343 21344 } 21345 21346 fnSuperClass.call(this, oConfig.element, oConfig.attributes); 21347 21348 } 21349 21350 } 21351 21352 }; 21353 21354 21355 21356 YAHOO.extend(YAHOO.widget.Button, YAHOO.util.Element, { 21357 21358 21359 // Protected properties 21360 21361 21362 /** 21363 * @property _button 21364 * @description Object reference to the button's internal 21365 * <code><a></code> or <code><button></code> element. 21366 * @default null 21367 * @protected 21368 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 21369 * level-one-html.html#ID-48250443">HTMLAnchorElement</a>|<a href=" 21370 * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html 21371 * #ID-34812697">HTMLButtonElement</a> 21372 */ 21373 _button: null, 21374 21375 21376 /** 21377 * @property _menu
21378 * @description Object reference to the button's menu. 21379 * @default null 21380 * @protected 21381 * @type {<a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>| 21382 * <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>} 21383 */ 21384 _menu: null, 21385 21386 21387 /** 21388 * @property _hiddenFields 21389 * @description Object reference to the <code><input></code> 21390 * element, or array of HTML form elements used to represent the button 21391 * when its parent form is submitted. 21392 * @default null 21393 * @protected 21394 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 21395 * level-one-html.html#ID-6043025">HTMLInputElement</a>|Array 21396 */ 21397 _hiddenFields: null, 21398 21399 21400 /** 21401 * @property _onclickAttributeValue 21402 * @description Object reference to the button's current value for the 21403 * "onclick" configuration attribute. 21404 * @default null 21405 * @protected 21406 * @type Object 21407 */ 21408 _onclickAttributeValue: null, 21409 21410 21411 /** 21412 * @property _activationKeyPressed 21413 * @description Boolean indicating if the key(s) that toggle the button's 21414 * "active" state have been pressed. 21415 * @default false 21416 * @protected 21417 * @type Boolean 21418 */ 21419 _activationKeyPressed: false, 21420 21421 21422 /** 21423 * @property _activationButtonPressed 21424 * @description Boolean indicating if the mouse button that toggles 21425 * the button's "active" state has been pressed. 21426 * @default false 21427 * @protected 21428 * @type Boolean 21429 */ 21430 _activationButtonPressed: false, 21431 21432 21433 /** 21434 * @property _hasKeyEventHandlers 21435 * @description Boolean indicating if the button's "blur", "keydown" and 21436 * "keyup" event handlers are assigned 21437 * @default false 21438 * @protected 21439 * @type Boolean 21440 */ 21441 _hasKeyEventHandlers: false, 21442 21443 21444 /** 21445 * @property _hasMouseEventHandlers 21446 * @description Boolean indicating if the button's "mouseout," 21447 * "mousedown," and "mouseup" event handlers are assigned 21448 * @default false 21449 * @protected 21450 * @type Boolean 21451 */ 21452 _hasMouseEventHandlers: false, 21453 21454 21455 /** 21456 * @property _nOptionRegionX 21457 * @description Number representing the X coordinate of the leftmost edge of the Button's 21458 * option region. Applies only to Buttons of type "split". 21459 * @default 0 21460 * @protected 21461 * @type Number 21462 */ 21463 _nOptionRegionX: 0, 21464 21465 21466 21467 // Constants 21468 21469 /** 21470 * @property CLASS_NAME_PREFIX 21471 * @description Prefix used for all class names applied to a Button. 21472 * @default "yui-" 21473 * @final
21474 * @type String 21475 */ 21476 CLASS_NAME_PREFIX: "yui-", 21477 21478 21479 /** 21480 * @property NODE_NAME 21481 * @description The name of the node to be used for the button's 21482 * root element. 21483 * @default "SPAN" 21484 * @final 21485 * @type String 21486 */ 21487 NODE_NAME: "SPAN", 21488 21489 21490 /** 21491 * @property CHECK_ACTIVATION_KEYS 21492 * @description Array of numbers representing keys that (when pressed) 21493 * toggle the button's "checked" attribute. 21494 * @default [32] 21495 * @final 21496 * @type Array 21497 */ 21498 CHECK_ACTIVATION_KEYS: [32], 21499 21500 21501 /** 21502 * @property ACTIVATION_KEYS 21503 * @description Array of numbers representing keys that (when presed) 21504 * toggle the button's "active" state. 21505 * @default [13, 32] 21506 * @final 21507 * @type Array 21508 */ 21509 ACTIVATION_KEYS: [13, 32], 21510 21511 21512 /** 21513 * @property OPTION_AREA_WIDTH 21514 * @description Width (in pixels) of the area of a split button that 21515 * when pressed will display a menu. 21516 * @default 20 21517 * @final 21518 * @type Number 21519 */ 21520 OPTION_AREA_WIDTH: 20, 21521 21522 21523 /** 21524 * @property CSS_CLASS_NAME 21525 * @description String representing the CSS class(es) to be applied to 21526 * the button's root element. 21527 * @default "button" 21528 * @final
21529 * @type String 21530 */ 21531 CSS_CLASS_NAME: "button", 21532 21533 21534 21535 // Protected attribute setter methods 21536 21537 21538 /** 21539 * @method _setType 21540 * @description Sets the value of the button's "type" attribute. 21541 * @protected 21542 * @param {String} p_sType String indicating the value for the button's 21543 * "type" attribute. 21544 */ 21545 _setType: function (p_sType) { 21546 21547 if (p_sType == "split") { 21548 21549 this.on("option", this._onOption); 21550 21551 } 21552 21553 }, 21554 21555 21556 /** 21557 * @method _setLabel 21558 * @description Sets the value of the button's "label" attribute. 21559 * @protected 21560 * @param {HTML} p_sLabel String indicating the value for the button's 21561 * "label" attribute. 21562 */ 21563 _setLabel: function (p_sLabel) { 21564 21565 this._button.innerHTML = p_sLabel; 21566 21567 21568 /* 21569 Remove and add the default class name from the root element 21570 for Gecko to ensure that the button shrinkwraps to the label. 21571 Without this the button will not be rendered at the correct 21572 width when the label changes. The most likely cause for this 21573 bug is button's use of the Gecko-specific CSS display type of 21574 "-moz-inline-box" to simulate "inline-block" supported by IE, 21575 Safari and Opera. 21576 */ 21577 21578 var sClass, 21579 nGeckoVersion = UA.gecko; 21580 21581 21582 if (nGeckoVersion && nGeckoVersion < 1.9 && Dom.inDocument(this.get("element"))) { 21583 21584 sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME); 21585 21586 this.removeClass(sClass); 21587 21588 Lang.later(0, this, this.addClass, sClass); 21589 21590 } 21591 21592 }, 21593 21594 21595 /** 21596 * @method _setTabIndex 21597 * @description Sets the value of the button's "tabindex" attribute. 21598 * @protected 21599 * @param {Number} p_nTabIndex Number indicating the value for the 21600 * button's "tabindex" attribute. 21601 */ 21602 _setTabIndex: function (p_nTabIndex) { 21603 21604 this._button.tabIndex = p_nTabIndex; 21605 21606 }, 21607 21608 21609 /** 21610 * @method _setTitle 21611 * @description Sets the value of the button's "title" attribute. 21612 * @protected 21613 * @param {String} p_nTabIndex Number indicating the value for 21614 * the button's "title" attribute. 21615 */ 21616 _setTitle: function (p_sTitle) { 21617 21618 if (this.get("type") != "link") { 21619 21620 this._button.title = p_sTitle; 21621 21622 } 21623 21624 }, 21625 21626 21627 /** 21628 * @method _setDisabled 21629 * @description Sets the value of the button's "disabled" attribute. 21630 * @protected 21631 * @param {Boolean} p_bDisabled Boolean indicating the value for 21632 * the button's "disabled" attribute. 21633 */ 21634 _setDisabled: function (p_bDisabled) { 21635 21636 if (this.get("type") != "link") { 21637 21638 if (p_bDisabled) { 21639 21640 if (this._menu) { 21641 21642 this._menu.hide(); 21643 21644 } 21645 21646 if (this.hasFocus()) { 21647 21648 this.blur(); 21649 21650 } 21651 21652 this._button.setAttribute("disabled", "disabled"); 21653 21654 this.addStateCSSClasses("disabled"); 21655 21656 this.removeStateCSSClasses("hover"); 21657 this.removeStateCSSClasses("active"); 21658 this.removeStateCSSClasses("focus"); 21659 21660 } 21661 else { 21662 21663 this._button.removeAttribute("disabled"); 21664 21665 this.removeStateCSSClasses("disabled"); 21666 21667 } 21668 21669 } 21670 21671 }, 21672 21673 21674 /** 21675 * @method _setHref
21676 * @description Sets the value of the button's "href" attribute. 21677 * @protected 21678 * @param {String} p_sHref String indicating the value for the button's 21679 * "href" attribute. 21680 */ 21681 _setHref: function (p_sHref) { 21682 21683 if (this.get("type") == "link") { 21684 21685 this._button.href = p_sHref; 21686 21687 } 21688 21689 }, 21690 21691 21692 /** 21693 * @method _setTarget 21694 * @description Sets the value of the button's "target" attribute. 21695 * @protected 21696 * @param {String} p_sTarget String indicating the value for the button's 21697 * "target" attribute. 21698 */ 21699 _setTarget: function (p_sTarget) { 21700 21701 if (this.get("type") == "link") { 21702 21703 this._button.setAttribute("target", p_sTarget); 21704 21705 } 21706 21707 }, 21708 21709 21710 /** 21711 * @method _setChecked 21712 * @description Sets the value of the button's "target" attribute. 21713 * @protected 21714 * @param {Boolean} p_bChecked Boolean indicating the value for 21715 * the button's "checked" attribute. 21716 */ 21717 _setChecked: function (p_bChecked) { 21718 21719 var sType = this.get("type"); 21720 21721 if (sType == "checkbox" || sType == "radio") { 21722 21723 if (p_bChecked) { 21724 this.addStateCSSClasses("checked"); 21725 } 21726 else { 21727 this.removeStateCSSClasses("checked"); 21728 } 21729 21730 } 21731 21732 }, 21733 21734 21735 /** 21736 * @method _setMenu 21737 * @description Sets the value of the button's "menu" attribute. 21738 * @protected 21739 * @param {Object} p_oMenu Object indicating the value for the button's 21740 * "menu" attribute. 21741 */ 21742 _setMenu: function (p_oMenu) { 21743 21744 var bLazyLoad = this.get("lazyloadmenu"), 21745 oButtonElement = this.get("element"), 21746 sMenuCSSClassName, 21747 21748 /* 21749 Boolean indicating if the value of p_oMenu is an instance 21750 of YAHOO.widget.Menu or YAHOO.widget.Overlay. 21751 */ 21752 21753 bInstance = false, 21754 oMenu, 21755 oMenuElement, 21756 oSrcElement; 21757 21758 21759 function onAppendTo() { 21760 21761 oMenu.render(oButtonElement.parentNode); 21762 21763 this.removeListener("appendTo", onAppendTo); 21764 21765 } 21766 21767 21768 function setMenuContainer() { 21769 21770 oMenu.cfg.queueProperty("container", oButtonElement.parentNode); 21771 21772 this.removeListener("appendTo", setMenuContainer); 21773 21774 } 21775 21776 21777 function initMenu() { 21778 21779 var oContainer; 21780 21781 if (oMenu) { 21782 21783 Dom.addClass(oMenu.element, this.get("menuclassname")); 21784 Dom.addClass(oMenu.element, this.CLASS_NAME_PREFIX + this.get("type") + "-button-menu"); 21785 21786 oMenu.showEvent.subscribe(this._onMenuShow, null, this); 21787 oMenu.hideEvent.subscribe(this._onMenuHide, null, this); 21788 oMenu.renderEvent.subscribe(this._onMenuRender, null, this); 21789 21790 21791 if (Menu && oMenu instanceof Menu) { 21792 21793 if (bLazyLoad) { 21794 21795 oContainer = this.get("container"); 21796 21797 if (oContainer) { 21798 21799 oMenu.cfg.queueProperty("container", oContainer); 21800 21801 } 21802 else { 21803 21804 this.on("appendTo", setMenuContainer); 21805 21806 } 21807 21808 } 21809 21810 oMenu.cfg.queueProperty("clicktohide", false); 21811 21812 oMenu.keyDownEvent.subscribe(this._onMenuKeyDown, this, true); 21813 oMenu.subscribe("click", this._onMenuClick, this, true); 21814 21815 this.on("selectedMenuItemChange", this._onSelectedMenuItemChange); 21816 21817 oSrcElement = oMenu.srcElement; 21818 21819 if (oSrcElement && oSrcElement.nodeName.toUpperCase() == "SELECT") { 21820 21821 oSrcElement.style.display = "none"; 21822 oSrcElement.parentNode.removeChild(oSrcElement); 21823 21824 } 21825 21826 } 21827 else if (Overlay && oMenu instanceof Overlay) { 21828 21829 if (!m_oOverlayManager) { 21830 21831 m_oOverlayManager = new YAHOO.widget.OverlayManager(); 21832 21833 } 21834 21835 m_oOverlayManager.register(oMenu); 21836 21837 } 21838 21839 21840 this._menu = oMenu; 21841 21842 21843 if (!bInstance && !bLazyLoad) { 21844 21845 if (Dom.inDocument(oButtonElement)) { 21846 21847 oMenu.render(oButtonElement.parentNode); 21848 21849 } 21850 else { 21851 21852 this.on("appendTo", onAppendTo); 21853 21854 } 21855 21856 } 21857 21858 } 21859 21860 } 21861 21862 21863 if (Overlay) { 21864 21865 if (Menu) { 21866 21867 sMenuCSSClassName = Menu.prototype.CSS_CLASS_NAME; 21868 21869 } 21870 21871 if (p_oMenu && Menu && (p_oMenu instanceof Menu)) { 21872 21873 oMenu = p_oMenu; 21874 bInstance = true; 21875 21876 initMenu.call(this); 21877 21878 } 21879 else if (Overlay && p_oMenu && (p_oMenu instanceof Overlay)) { 21880 21881 oMenu = p_oMenu; 21882 bInstance = true; 21883 21884 oMenu.cfg.queueProperty("visible", false); 21885 21886 initMenu.call(this); 21887 21888 } 21889 else if (Menu && Lang.isArray(p_oMenu)) { 21890
21891 oMenu = new Menu(Dom.generateId(), { lazyload: bLazyLoad, itemdata: p_oMenu }); 21892 21893 this._menu = oMenu; 21894 21895 this.on("appendTo", initMenu); 21896 21897 } 21898 else if (Lang.isString(p_oMenu)) { 21899 21900 oMenuElement = Dom.get(p_oMenu); 21901 21902 if (oMenuElement) { 21903 21904 if (Menu && Dom.hasClass(oMenuElement, sMenuCSSClassName) || 21905 oMenuElement.nodeName.toUpperCase() == "SELECT") { 21906 21907 oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad }); 21908 21909 initMenu.call(this); 21910 21911 } 21912 else if (Overlay) { 21913 21914 oMenu = new Overlay(p_oMenu, { visible: false }); 21915 21916 initMenu.call(this); 21917 21918 } 21919 21920 } 21921 21922 } 21923 else if (p_oMenu && p_oMenu.nodeName) { 21924 21925 if (Menu && Dom.hasClass(p_oMenu, sMenuCSSClassName) || 21926 p_oMenu.nodeName.toUpperCase() == "SELECT") { 21927 21928 oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad }); 21929 21930 initMenu.call(this); 21931 21932 } 21933 else if (Overlay) { 21934 21935 if (!p_oMenu.id) { 21936 21937 Dom.generateId(p_oMenu); 21938 21939 } 21940 21941 oMenu = new Overlay(p_oMenu, { visible: false }); 21942 21943 initMenu.call(this); 21944 21945 } 21946 21947 } 21948 21949 } 21950 21951 }, 21952 21953 21954 /** 21955 * @method _setOnClick 21956 * @description Sets the value of the button's "onclick" attribute. 21957 * @protected 21958 * @param {Object} p_oObject Object indicating the value for the button's 21959 * "onclick" attribute. 21960 */ 21961 _setOnClick: function (p_oObject) { 21962 21963 /* 21964 Remove any existing listeners if a "click" event handler 21965 has already been specified. 21966 */ 21967 21968 if (this._onclickAttributeValue && 21969 (this._onclickAttributeValue != p_oObject)) { 21970 21971 this.removeListener("click", this._onclickAttributeValue.fn); 21972 21973 this._onclickAttributeValue = null; 21974 21975 } 21976 21977 21978 if (!this._onclickAttributeValue && 21979 Lang.isObject(p_oObject) && 21980 Lang.isFunction(p_oObject.fn)) { 21981 21982 this.on("click", p_oObject.fn, p_oObject.obj, p_oObject.scope); 21983 21984 this._onclickAttributeValue = p_oObject; 21985 21986 } 21987 21988 }, 21989 21990 21991 21992 // Protected methods 21993 21994 21995 21996 /** 21997 * @method _isActivationKey 21998 * @description Determines if the specified keycode is one that toggles 21999 * the button's "active" state. 22000 * @protected 22001 * @param {Number} p_nKeyCode Number representing the keycode to 22002 * be evaluated. 22003 * @return {Boolean} 22004 */ 22005 _isActivationKey: function (p_nKeyCode) { 22006 22007 var sType = this.get("type"), 22008 aKeyCodes = (sType == "checkbox" || sType == "radio") ? 22009 this.CHECK_ACTIVATION_KEYS : this.ACTIVATION_KEYS, 22010 22011 nKeyCodes = aKeyCodes.length, 22012 bReturnVal = false, 22013 i; 22014 22015 22016 if (nKeyCodes > 0) { 22017 22018 i = nKeyCodes - 1; 22019 22020 do { 22021 22022 if (p_nKeyCode == aKeyCodes[i]) { 22023 22024 bReturnVal = true; 22025 break; 22026 22027 } 22028 22029 } 22030 while (i--); 22031 22032 } 22033 22034 return bReturnVal; 22035 22036 }, 22037 22038 22039 /** 22040 * @method _isSplitButtonOptionKey 22041 * @description Determines if the specified keycode is one that toggles 22042 * the display of the split button's menu. 22043 * @protected 22044 * @param {Event} p_oEvent Object representing the DOM event object 22045 * passed back by the event utility (YAHOO.util.Event). 22046 * @return {Boolean} 22047 */ 22048 _isSplitButtonOptionKey: function (p_oEvent) { 22049 22050 var bShowMenu = (Event.getCharCode(p_oEvent) == 40); 22051 22052 22053 var onKeyPress = function (p_oEvent) { 22054 22055 Event.preventDefault(p_oEvent); 22056 22057 this.removeListener("keypress", onKeyPress); 22058 22059 }; 22060 22061 22062 // Prevent the browser from scrolling the window 22063 if (bShowMenu) { 22064 22065 if (UA.opera) { 22066 22067 this.on("keypress", onKeyPress); 22068 22069 } 22070 22071 Event.preventDefault(p_oEvent); 22072 } 22073 22074 return bShowMenu; 22075 22076 }, 22077 22078 22079 /** 22080 * @method _addListenersToForm
22081 * @description Adds event handlers to the button's form. 22082 * @protected 22083 */ 22084 _addListenersToForm: function () { 22085 22086 var oForm = this.getForm(), 22087 onFormKeyPress = YAHOO.widget.Button.onFormKeyPress, 22088 bHasKeyPressListener, 22089 oSrcElement, 22090 aListeners, 22091 nListeners, 22092 i; 22093 22094 22095 if (oForm) { 22096 22097 Event.on(oForm, "reset", this._onFormReset, null, this); 22098 Event.on(oForm, "submit", this._onFormSubmit, null, this); 22099 22100 oSrcElement = this.get("srcelement"); 22101 22102 22103 if (this.get("type") == "submit" || 22104 (oSrcElement && oSrcElement.type == "submit")) 22105 { 22106 22107 aListeners = Event.getListeners(oForm, "keypress"); 22108 bHasKeyPressListener = false; 22109 22110 if (aListeners) { 22111 22112 nListeners = aListeners.length; 22113 22114 if (nListeners > 0) { 22115 22116 i = nListeners - 1; 22117 22118 do { 22119 22120 if (aListeners[i].fn == onFormKeyPress) { 22121 22122 bHasKeyPressListener = true; 22123 break; 22124 22125 } 22126 22127 } 22128 while (i--); 22129 22130 } 22131 22132 } 22133 22134 22135 if (!bHasKeyPressListener) { 22136 22137 Event.on(oForm, "keypress", onFormKeyPress); 22138 22139 } 22140 22141 } 22142 22143 } 22144 22145 }, 22146 22147 22148 22149 /** 22150 * @method _showMenu 22151 * @description Shows the button's menu. 22152 * @protected 22153 * @param {Event} p_oEvent Object representing the DOM event object 22154 * passed back by the event utility (YAHOO.util.Event) that triggered 22155 * the display of the menu. 22156 */ 22157 _showMenu: function (p_oEvent) { 22158 22159 if (YAHOO.widget.MenuManager) { 22160 YAHOO.widget.MenuManager.hideVisible(); 22161 } 22162 22163 22164 if (m_oOverlayManager) { 22165 m_oOverlayManager.hideAll(); 22166 } 22167 22168 22169 var oMenu = this._menu, 22170 aMenuAlignment = this.get("menualignment"), 22171 bFocusMenu = this.get("focusmenu"), 22172 fnFocusMethod; 22173 22174 22175 if (this._renderedMenu) { 22176 22177 oMenu.cfg.setProperty("context", 22178 [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]); 22179 22180 oMenu.cfg.setProperty("preventcontextoverlap", true); 22181 oMenu.cfg.setProperty("constraintoviewport", true); 22182 22183 } 22184 else { 22185 22186 oMenu.cfg.queueProperty("context", 22187 [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]); 22188 22189 oMenu.cfg.queueProperty("preventcontextoverlap", true); 22190 oMenu.cfg.queueProperty("constraintoviewport", true); 22191 22192 } 22193 22194 22195 /* 22196 Refocus the Button before showing its Menu in case the call to 22197 YAHOO.widget.MenuManager.hideVisible() resulted in another element in the 22198 DOM being focused after another Menu was hidden. 22199 */ 22200 22201 this.focus(); 22202 22203 22204 if (Menu && oMenu && (oMenu instanceof Menu)) { 22205 22206 // Since Menus automatically focus themselves when made visible, temporarily 22207 // replace the Menu focus method so that the value of the Button's "focusmenu" 22208 // attribute determines if the Menu should be focus when made visible. 22209 22210 fnFocusMethod = oMenu.focus; 22211 22212 oMenu.focus = function () {}; 22213 22214 if (this._renderedMenu) { 22215 22216 oMenu.cfg.setProperty("minscrollheight", this.get("menuminscrollheight")); 22217 oMenu.cfg.setProperty("maxheight", this.get("menumaxheight")); 22218 22219 } 22220 else { 22221 22222 oMenu.cfg.queueProperty("minscrollheight", this.get("menuminscrollheight")); 22223 oMenu.cfg.queueProperty("maxheight", this.get("menumaxheight")); 22224 22225 } 22226 22227 22228 oMenu.show(); 22229
22230 oMenu.focus = fnFocusMethod; 22231 22232 oMenu.align(); 22233 22234 22235 /* 22236 Stop the propagation of the event so that the MenuManager 22237 doesn't blur the menu after it gets focus. 22238 */ 22239 22240 if (p_oEvent.type == "mousedown") { 22241 Event.stopPropagation(p_oEvent); 22242 } 22243 22244 22245 if (bFocusMenu) { 22246 oMenu.focus(); 22247 } 22248 22249 } 22250 else if (Overlay && oMenu && (oMenu instanceof Overlay)) { 22251 22252 if (!this._renderedMenu) { 22253 oMenu.render(this.get("element").parentNode); 22254 } 22255 22256 oMenu.show(); 22257 oMenu.align(); 22258 22259 } 22260 22261 }, 22262 22263 22264 /** 22265 * @method _hideMenu 22266 * @description Hides the button's menu. 22267 * @protected 22268 */ 22269 _hideMenu: function () { 22270 22271 var oMenu = this._menu; 22272 22273 if (oMenu) { 22274 22275 oMenu.hide(); 22276 22277 } 22278 22279 }, 22280 22281 22282 22283 22284 // Protected event handlers 22285 22286 22287 /** 22288 * @method _onMouseOver 22289 * @description "mouseover" event handler for the button. 22290 * @protected 22291 * @param {Event} p_oEvent Object representing the DOM event object 22292 * passed back by the event utility (YAHOO.util.Event). 22293 */ 22294 _onMouseOver: function (p_oEvent) { 22295 22296 var sType = this.get("type"), 22297 oElement, 22298 nOptionRegionX; 22299 22300 22301 if (sType === "split") { 22302 22303 oElement = this.get("element"); 22304 nOptionRegionX = 22305 (Dom.getX(oElement) + (oElement.offsetWidth - this.OPTION_AREA_WIDTH)); 22306 22307 this._nOptionRegionX = nOptionRegionX; 22308 22309 } 22310 22311 22312 if (!this._hasMouseEventHandlers) { 22313 22314 if (sType === "split") { 22315 22316 this.on("mousemove", this._onMouseMove); 22317 22318 } 22319 22320 this.on("mouseout", this._onMouseOut); 22321 22322 this._hasMouseEventHandlers = true; 22323 22324 } 22325 22326 22327 this.addStateCSSClasses("hover"); 22328 22329 22330 if (sType === "split" && (Event.getPageX(p_oEvent) > nOptionRegionX)) { 22331 22332 this.addStateCSSClasses("hoveroption"); 22333 22334 } 22335 22336 22337 if (this._activationButtonPressed) { 22338 22339 this.addStateCSSClasses("active"); 22340 22341 } 22342 22343 22344 if (this._bOptionPressed) { 22345 22346 this.addStateCSSClasses("activeoption"); 22347 22348 } 22349 22350 22351 if (this._activationButtonPressed || this._bOptionPressed) { 22352 22353 Event.removeListener(document, "mouseup", this._onDocumentMouseUp); 22354 22355 } 22356 22357 }, 22358 22359 22360 /** 22361 * @method _onMouseMove 22362 * @description "mousemove" event handler for the button. 22363 * @protected 22364 * @param {Event} p_oEvent Object representing the DOM event object 22365 * passed back by the event utility (YAHOO.util.Event). 22366 */ 22367 _onMouseMove: function (p_oEvent) { 22368 22369 var nOptionRegionX = this._nOptionRegionX; 22370 22371 if (nOptionRegionX) { 22372 22373 if (Event.getPageX(p_oEvent) > nOptionRegionX) { 22374 22375 this.addStateCSSClasses("hoveroption"); 22376 22377 } 22378 else { 22379 22380 this.removeStateCSSClasses("hoveroption"); 22381 22382 } 22383 22384 } 22385 22386 }, 22387 22388 /** 22389 * @method _onMouseOut 22390 * @description "mouseout" event handler for the button. 22391 * @protected 22392 * @param {Event} p_oEvent Object representing the DOM event object 22393 * passed back by the event utility (YAHOO.util.Event). 22394 */ 22395 _onMouseOut: function (p_oEvent) { 22396 22397 var sType = this.get("type"); 22398 22399 this.removeStateCSSClasses("hover"); 22400 22401 22402 if (sType != "menu") { 22403 22404 this.removeStateCSSClasses("active"); 22405 22406 } 22407 22408 22409 if (this._activationButtonPressed || this._bOptionPressed) { 22410 22411 Event.on(document, "mouseup", this._onDocumentMouseUp, null, this); 22412 22413 } 22414 22415 22416 if (sType === "split" && (Event.getPageX(p_oEvent) > this._nOptionRegionX)) { 22417
22418 this.removeStateCSSClasses("hoveroption"); 22419 22420 } 22421 22422 }, 22423 22424 22425 /** 22426 * @method _onDocumentMouseUp 22427 * @description "mouseup" event handler for the button. 22428 * @protected 22429 * @param {Event} p_oEvent Object representing the DOM event object 22430 * passed back by the event utility (YAHOO.util.Event). 22431 */ 22432 _onDocumentMouseUp: function (p_oEvent) { 22433 22434 this._activationButtonPressed = false; 22435 this._bOptionPressed = false; 22436 22437 var sType = this.get("type"), 22438 oTarget, 22439 oMenuElement; 22440 22441 if (sType == "menu" || sType == "split") { 22442 22443 oTarget = Event.getTarget(p_oEvent); 22444 oMenuElement = this._menu.element; 22445 22446 if (oTarget != oMenuElement && 22447 !Dom.isAncestor(oMenuElement, oTarget)) { 22448 22449 this.removeStateCSSClasses((sType == "menu" ? 22450 "active" : "activeoption")); 22451 22452 this._hideMenu(); 22453 22454 } 22455 22456 } 22457 22458 Event.removeListener(document, "mouseup", this._onDocumentMouseUp); 22459 22460 }, 22461 22462 22463 /** 22464 * @method _onMouseDown 22465 * @description "mousedown" event handler for the button. 22466 * @protected 22467 * @param {Event} p_oEvent Object representing the DOM event object 22468 * passed back by the event utility (YAHOO.util.Event). 22469 */ 22470 _onMouseDown: function (p_oEvent) { 22471 22472 var sType, 22473 bReturnVal = true; 22474 22475 22476 function onMouseUp() { 22477 22478 this._hideMenu(); 22479 this.removeListener("mouseup", onMouseUp); 22480 22481 } 22482 22483 22484 if ((p_oEvent.which || p_oEvent.button) == 1) { 22485 22486 22487 if (!this.hasFocus()) { 22488 Lang.later(0, this, this.focus); 22489 //this.focus(); 22490 } 22491 22492 22493 sType = this.get("type"); 22494 22495 22496 if (sType == "split") { 22497 22498 if (Event.getPageX(p_oEvent) > this._nOptionRegionX) { 22499 22500 this.fireEvent("option", p_oEvent); 22501 bReturnVal = false;
22502 22503 } 22504 else { 22505 22506 this.addStateCSSClasses("active"); 22507 22508 this._activationButtonPressed = true; 22509 22510 } 22511 22512 } 22513 else if (sType == "menu") { 22514 22515 if (this.isActive()) { 22516 22517 this._hideMenu(); 22518 22519 this._activationButtonPressed = false; 22520 22521 } 22522 else { 22523 22524 this._showMenu(p_oEvent); 22525 22526 this._activationButtonPressed = true; 22527 22528 } 22529 22530 } 22531 else { 22532 22533 this.addStateCSSClasses("active"); 22534 22535 this._activationButtonPressed = true; 22536 22537 } 22538 22539 22540 22541 if (sType == "split" || sType == "menu") { 22542 22543 this._hideMenuTimer = Lang.later(250, this, this.on, ["mouseup", onMouseUp]); 22544 22545 } 22546 22547 } 22548 22549 return bReturnVal; 22550 22551 }, 22552 22553 22554 /** 22555 * @method _onMouseUp 22556 * @description "mouseup" event handler for the button. 22557 * @protected 22558 * @param {Event} p_oEvent Object representing the DOM event object 22559 * passed back by the event utility (YAHOO.util.Event). 22560 */ 22561 _onMouseUp: function (p_oEvent) { 22562 this.inMouseDown = false; 22563 22564 var sType = this.get("type"), 22565 oHideMenuTimer = this._hideMenuTimer, 22566 bReturnVal = true; 22567 22568 22569 if (oHideMenuTimer) { 22570 22571 oHideMenuTimer.cancel(); 22572 22573 } 22574 22575 22576 if (sType == "checkbox" || sType == "radio") { 22577 if ((p_oEvent.which || p_oEvent.button) != 1) { 22578 return; 22579 } 22580 22581 this.set("checked", !(this.get("checked"))); 22582 22583 } 22584 22585 22586 this._activationButtonPressed = false; 22587 22588 22589 if (sType != "menu") { 22590 22591 this.removeStateCSSClasses("active"); 22592 22593 } 22594 22595 22596 if (sType == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) { 22597 22598 bReturnVal = false;
22599 22600 } 22601 22602 return bReturnVal; 22603 22604 }, 22605 22606 22607 /** 22608 * @method _onFocus 22609 * @description "focus" event handler for the button. 22610 * @protected 22611 * @param {Event} p_oEvent Object representing the DOM event object 22612 * passed back by the event utility (YAHOO.util.Event). 22613 */ 22614 _onFocus: function (p_oEvent) { 22615 22616 var oElement; 22617 22618 this.addStateCSSClasses("focus"); 22619 22620 if (this._activationKeyPressed) { 22621 22622 this.addStateCSSClasses("active"); 22623 22624 } 22625 22626 m_oFocusedButton = this; 22627 22628 22629 if (!this._hasKeyEventHandlers) { 22630 22631 oElement = this._button; 22632 22633 Event.on(oElement, "blur", this._onBlur, null, this); 22634 Event.on(oElement, "keydown", this._onKeyDown, null, this); 22635 Event.on(oElement, "keyup", this._onKeyUp, null, this); 22636 22637 this._hasKeyEventHandlers = true; 22638 22639 } 22640 22641 22642 this.fireEvent("focus", p_oEvent); 22643 22644 }, 22645 22646 22647 /** 22648 * @method _onBlur 22649 * @description "blur" event handler for the button. 22650 * @protected 22651 * @param {Event} p_oEvent Object representing the DOM event object 22652 * passed back by the event utility (YAHOO.util.Event). 22653 */ 22654 _onBlur: function (p_oEvent) { 22655 22656 this.removeStateCSSClasses("focus"); 22657 22658 if (this.get("type") != "menu") { 22659 22660 this.removeStateCSSClasses("active"); 22661 22662 } 22663 22664 if (this._activationKeyPressed) { 22665 22666 Event.on(document, "keyup", this._onDocumentKeyUp, null, this); 22667 22668 } 22669 22670 22671 m_oFocusedButton = null; 22672 22673 this.fireEvent("blur", p_oEvent); 22674 22675 }, 22676 22677 22678 /** 22679 * @method _onDocumentKeyUp 22680 * @description "keyup" event handler for the document. 22681 * @protected 22682 * @param {Event} p_oEvent Object representing the DOM event object 22683 * passed back by the event utility (YAHOO.util.Event). 22684 */ 22685 _onDocumentKeyUp: function (p_oEvent) { 22686 22687 if (this._isActivationKey(Event.getCharCode(p_oEvent))) { 22688 22689 this._activationKeyPressed = false; 22690 22691 Event.removeListener(document, "keyup", this._onDocumentKeyUp); 22692 22693 } 22694 22695 }, 22696 22697 22698 /** 22699 * @method _onKeyDown 22700 * @description "keydown" event handler for the button. 22701 * @protected 22702 * @param {Event} p_oEvent Object representing the DOM event object 22703 * passed back by the event utility (YAHOO.util.Event). 22704 */ 22705 _onKeyDown: function (p_oEvent) { 22706 22707 var oMenu = this._menu; 22708 22709 22710 if (this.get("type") == "split" && 22711 this._isSplitButtonOptionKey(p_oEvent)) { 22712 22713 this.fireEvent("option", p_oEvent); 22714 22715 } 22716 else if (this._isActivationKey(Event.getCharCode(p_oEvent))) { 22717 22718 if (this.get("type") == "menu") { 22719 22720 this._showMenu(p_oEvent); 22721 22722 } 22723 else { 22724 22725 this._activationKeyPressed = true; 22726 22727 this.addStateCSSClasses("active"); 22728 22729 } 22730 22731 } 22732 22733 22734 if (oMenu && oMenu.cfg.getProperty("visible") && 22735 Event.getCharCode(p_oEvent) == 27) { 22736 22737 oMenu.hide(); 22738 this.focus(); 22739 22740 } 22741 22742 }, 22743 22744 22745 /** 22746 * @method _onKeyUp 22747 * @description "keyup" event handler for the button. 22748 * @protected 22749 * @param {Event} p_oEvent Object representing the DOM event object 22750 * passed back by the event utility (YAHOO.util.Event). 22751 */ 22752 _onKeyUp: function (p_oEvent) { 22753 22754 var sType; 22755 22756 if (this._isActivationKey(Event.getCharCode(p_oEvent))) { 22757 22758 sType = this.get("type"); 22759 22760 if (sType == "checkbox" || sType == "radio") { 22761 22762 this.set("checked", !(this.get("checked"))); 22763 22764 } 22765 22766 this._activationKeyPressed = false;
22767 22768 if (this.get("type") != "menu") { 22769 22770 this.removeStateCSSClasses("active"); 22771 22772 } 22773 22774 } 22775 22776 }, 22777 22778 22779 /** 22780 * @method _onClick 22781 * @description "click" event handler for the button. 22782 * @protected 22783 * @param {Event} p_oEvent Object representing the DOM event object 22784 * passed back by the event utility (YAHOO.util.Event). 22785 */ 22786 _onClick: function (p_oEvent) { 22787 22788 var sType = this.get("type"), 22789 oForm, 22790 oSrcElement, 22791 bReturnVal; 22792 22793 22794 switch (sType) { 22795 22796 case "submit": 22797 22798 if (p_oEvent.returnValue !== false) { 22799 22800 this.submitForm(); 22801 22802 } 22803 22804 break; 22805 22806 case "reset": 22807 22808 oForm = this.getForm(); 22809 22810 if (oForm) { 22811 22812 oForm.reset(); 22813 22814 } 22815 22816 break; 22817 22818 22819 case "split": 22820 22821 if (this._nOptionRegionX > 0 && 22822 (Event.getPageX(p_oEvent) > this._nOptionRegionX)) { 22823 22824 bReturnVal = false;
22825 22826 } 22827 else { 22828 22829 this._hideMenu(); 22830 22831 oSrcElement = this.get("srcelement"); 22832 22833 if (oSrcElement && oSrcElement.type == "submit" && 22834 p_oEvent.returnValue !== false) { 22835 22836 this.submitForm(); 22837 22838 } 22839 22840 } 22841 22842 break; 22843 22844 } 22845 22846 return bReturnVal; 22847 22848 }, 22849 22850 22851 /** 22852 * @method _onDblClick 22853 * @description "dblclick" event handler for the button. 22854 * @protected 22855 * @param {Event} p_oEvent Object representing the DOM event object 22856 * passed back by the event utility (YAHOO.util.Event). 22857 */ 22858 _onDblClick: function (p_oEvent) { 22859 22860 var bReturnVal = true; 22861 22862 if (this.get("type") == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) { 22863 22864 bReturnVal = false; 22865 22866 } 22867 22868 return bReturnVal; 22869 22870 }, 22871 22872 22873 /** 22874 * @method _onAppendTo 22875 * @description "appendTo" event handler for the button. 22876 * @protected 22877 * @param {Event} p_oEvent Object representing the DOM event object 22878 * passed back by the event utility (YAHOO.util.Event). 22879 */ 22880 _onAppendTo: function (p_oEvent) { 22881 22882 /* 22883 It is necessary to call "_addListenersToForm" using 22884 "setTimeout" to make sure that the button's "form" property 22885 returns a node reference. Sometimes, if you try to get the 22886 reference immediately after appending the field, it is null. 22887 */ 22888 22889 Lang.later(0, this, this._addListenersToForm); 22890 22891 }, 22892 22893 22894 /** 22895 * @method _onFormReset 22896 * @description "reset" event handler for the button's form. 22897 * @protected 22898 * @param {Event} p_oEvent Object representing the DOM event 22899 * object passed back by the event utility (YAHOO.util.Event). 22900 */ 22901 _onFormReset: function (p_oEvent) { 22902 22903 var sType = this.get("type"), 22904 oMenu = this._menu; 22905 22906 if (sType == "checkbox" || sType == "radio") { 22907 22908 this.resetValue("checked"); 22909 22910 } 22911 22912 22913 if (Menu && oMenu && (oMenu instanceof Menu)) { 22914 22915 this.resetValue("selectedMenuItem"); 22916 22917 } 22918 22919 }, 22920 22921 22922 /** 22923 * @method _onFormSubmit 22924 * @description "submit" event handler for the button's form. 22925 * @protected 22926 * @param {Event} p_oEvent Object representing the DOM event 22927 * object passed back by the event utility (YAHOO.util.Event). 22928 */ 22929 _onFormSubmit: function (p_oEvent) { 22930 22931 this.createHiddenFields(); 22932 22933 }, 22934 22935 22936 /** 22937 * @method _onDocumentMouseDown 22938 * @description "mousedown" event handler for the document. 22939 * @protected 22940 * @param {Event} p_oEvent Object representing the DOM event object 22941 * passed back by the event utility (YAHOO.util.Event). 22942 */ 22943 _onDocumentMouseDown: function (p_oEvent) { 22944 22945 var oTarget = Event.getTarget(p_oEvent), 22946 oButtonElement = this.get("element"), 22947 oMenuElement = this._menu.element; 22948 22949 function findTargetInSubmenus(aSubmenus) { 22950 var i, iMax, oSubmenuElement; 22951 if (!aSubmenus) { 22952 return true; 22953 } 22954 for (i = 0, iMax = aSubmenus.length; i < iMax; i++) { 22955 oSubmenuElement = aSubmenus[i].element; 22956 if (oTarget == oSubmenuElement || Dom.isAncestor(oSubmenuElement, oTarget)) { 22957 return true; 22958 } 22959 if (aSubmenus[i] && aSubmenus[i].getSubmenus) { 22960 if (findTargetInSubmenus(aSubmenus[i].getSubmenus())) { 22961 return true; 22962 } 22963 } 22964 } 22965 22966 return false; 22967 } 22968 22969 if (oTarget != oButtonElement && 22970 !Dom.isAncestor(oButtonElement, oTarget) && 22971 oTarget != oMenuElement && 22972 !Dom.isAncestor(oMenuElement, oTarget)) { 22973 22974 22975 if (this._menu && this._menu.getSubmenus) { 22976 if (findTargetInSubmenus(this._menu.getSubmenus())) { 22977 return; 22978 } 22979 } 22980 22981 22982 this._hideMenu(); 22983 22984 // In IE when the user mouses down on a focusable element 22985 // that element will be focused and become the "activeElement".
22986 // (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx) 22987 // However, there is a bug in IE where if there is a 22988 // positioned element with a focused descendant that is 22989 // hidden in response to the mousedown event, the target of 22990 // the mousedown event will appear to have focus, but will 22991 // not be set as the activeElement. This will result 22992 // in the element not firing key events, even though it 22993 // appears to have focus. The following call to "setActive" 22994 // fixes this bug. 22995 22996 if (UA.ie && (UA.ie < 9) && oTarget.focus) { 22997 oTarget.setActive(); 22998 } 22999 23000 Event.removeListener(document, "mousedown", 23001 this._onDocumentMouseDown); 23002 23003 } 23004 23005 }, 23006 23007 23008 /** 23009 * @method _onOption 23010 * @description "option" event handler for the button. 23011 * @protected 23012 * @param {Event} p_oEvent Object representing the DOM event object 23013 * passed back by the event utility (YAHOO.util.Event). 23014 */ 23015 _onOption: function (p_oEvent) { 23016 23017 if (this.hasClass(this.CLASS_NAME_PREFIX + "split-button-activeoption")) { 23018 23019 this._hideMenu(); 23020 23021 this._bOptionPressed = false; 23022 23023 } 23024 else { 23025 23026 this._showMenu(p_oEvent); 23027 23028 this._bOptionPressed = true; 23029 23030 } 23031 23032 }, 23033 23034 23035 /** 23036 * @method _onMenuShow 23037 * @description "show" event handler for the button's menu. 23038 * @private 23039 * @param {String} p_sType String representing the name of the event 23040 * that was fired. 23041 */ 23042 _onMenuShow: function (p_sType) { 23043 23044 Event.on(document, "mousedown", this._onDocumentMouseDown, 23045 null, this); 23046 23047 var sState = (this.get("type") == "split") ? "activeoption" : "active"; 23048 23049 this.addStateCSSClasses(sState); 23050 23051 }, 23052 23053 23054 /** 23055 * @method _onMenuHide 23056 * @description "hide" event handler for the button's menu. 23057 * @private 23058 * @param {String} p_sType String representing the name of the event 23059 * that was fired. 23060 */ 23061 _onMenuHide: function (p_sType) { 23062 23063 var sState = (this.get("type") == "split") ? "activeoption" : "active"; 23064 23065 this.removeStateCSSClasses(sState); 23066 23067 23068 if (this.get("type") == "split") { 23069 23070 this._bOptionPressed = false; 23071 23072 } 23073 23074 }, 23075 23076 23077 /** 23078 * @method _onMenuKeyDown 23079 * @description "keydown" event handler for the button's menu. 23080 * @private 23081 * @param {String} p_sType String representing the name of the event 23082 * that was fired. 23083 * @param {Array} p_aArgs Array of arguments sent when the event 23084 * was fired. 23085 */ 23086 _onMenuKeyDown: function (p_sType, p_aArgs) { 23087 23088 var oEvent = p_aArgs[0]; 23089 23090 if (Event.getCharCode(oEvent) == 27) { 23091 23092 this.focus(); 23093 23094 if (this.get("type") == "split") { 23095 23096 this._bOptionPressed = false; 23097 23098 } 23099 23100 } 23101 23102 }, 23103 23104 23105 /** 23106 * @method _onMenuRender 23107 * @description "render" event handler for the button's menu. 23108 * @private 23109 * @param {String} p_sType String representing the name of the 23110 * event thatwas fired. 23111 */ 23112 _onMenuRender: function (p_sType) { 23113 23114 var oButtonElement = this.get("element"), 23115 oButtonParent = oButtonElement.parentNode, 23116 oMenu = this._menu, 23117 oMenuElement = oMenu.element, 23118 oSrcElement = oMenu.srcElement, 23119 oItem; 23120 23121 23122 if (oButtonParent != oMenuElement.parentNode) { 23123 23124 oButtonParent.appendChild(oMenuElement); 23125 23126 } 23127 23128 this._renderedMenu = true; 23129 23130 // If the user has designated an <option> of the Menu's source 23131 // <select> element to be selected, sync the selectedIndex with 23132 // the "selectedMenuItem" Attribute. 23133 23134 if (oSrcElement && 23135 oSrcElement.nodeName.toLowerCase() === "select" && 23136 oSrcElement.value) { 23137 23138 23139 oItem = oMenu.getItem(oSrcElement.selectedIndex); 23140 23141 // Set the value of the "selectedMenuItem" attribute 23142 // silently since this is the initial set--synchronizing 23143 // the value of the source <SELECT> element in the DOM with 23144 // its corresponding Menu instance. 23145 23146 this.set("selectedMenuItem", oItem, true); 23147 23148 // Call the "_onSelectedMenuItemChange" method since the 23149 // attribute was set silently. 23150 23151 this._onSelectedMenuItemChange({ newValue: oItem }); 23152 23153 } 23154 23155 }, 23156 23157 23158 23159 /** 23160 * @method _onMenuClick
23161 * @description "click" event handler for the button's menu. 23162 * @private 23163 * @param {String} p_sType String representing the name of the event 23164 * that was fired. 23165 * @param {Array} p_aArgs Array of arguments sent when the event 23166 * was fired. 23167 */ 23168 _onMenuClick: function (p_sType, p_aArgs) { 23169 23170 var oItem = p_aArgs[1], 23171 oSrcElement; 23172 23173 if (oItem) { 23174 23175 this.set("selectedMenuItem", oItem); 23176 23177 oSrcElement = this.get("srcelement"); 23178 23179 if (oSrcElement && oSrcElement.type == "submit") { 23180 23181 this.submitForm(); 23182 23183 } 23184 23185 this._hideMenu(); 23186 23187 } 23188 23189 }, 23190 23191 23192 /** 23193 * @method _onSelectedMenuItemChange 23194 * @description "selectedMenuItemChange" event handler for the Button's 23195 * "selectedMenuItem" attribute. 23196 * @param {Event} event Object representing the DOM event object 23197 * passed back by the event utility (YAHOO.util.Event). 23198 */ 23199 _onSelectedMenuItemChange: function (event) { 23200 23201 var oSelected = event.prevValue, 23202 oItem = event.newValue, 23203 sPrefix = this.CLASS_NAME_PREFIX; 23204 23205 if (oSelected) { 23206 Dom.removeClass(oSelected.element, (sPrefix + "button-selectedmenuitem")); 23207 } 23208 23209 if (oItem) { 23210 Dom.addClass(oItem.element, (sPrefix + "button-selectedmenuitem")); 23211 } 23212 23213 }, 23214 23215 23216 /** 23217 * @method _onLabelClick 23218 * @description "click" event handler for the Button's 23219 * <code><label></code> element. 23220 * @param {Event} event Object representing the DOM event object 23221 * passed back by the event utility (YAHOO.util.Event). 23222 */ 23223 _onLabelClick: function (event) { 23224 23225 this.focus(); 23226 23227 var sType = this.get("type"); 23228 23229 if (sType == "radio" || sType == "checkbox") { 23230 this.set("checked", (!this.get("checked"))); 23231 } 23232 23233 }, 23234 23235 23236 // Public methods 23237 23238 23239 /** 23240 * @method createButtonElement 23241 * @description Creates the button's HTML elements. 23242 * @param {String} p_sType String indicating the type of element 23243 * to create. 23244 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 23245 * level-one-html.html#ID-58190037">HTMLElement</a>} 23246 */ 23247 createButtonElement: function (p_sType) { 23248 23249 var sNodeName = this.NODE_NAME, 23250 oElement = document.createElement(sNodeName); 23251 23252 oElement.innerHTML = "<" + sNodeName + " class=\"first-child\">" + 23253 (p_sType == "link" ? "<a></a>" : 23254 "<button type=\"button\"></button>") + "</" + sNodeName + ">"; 23255 23256 return oElement; 23257 23258 }, 23259 23260 23261 /** 23262 * @method addStateCSSClasses 23263 * @description Appends state-specific CSS classes to the button's root 23264 * DOM element. 23265 */ 23266 addStateCSSClasses: function (p_sState) { 23267 23268 var sType = this.get("type"), 23269 sPrefix = this.CLASS_NAME_PREFIX; 23270 23271 if (Lang.isString(p_sState)) { 23272
23273 if (p_sState != "activeoption" && p_sState != "hoveroption") { 23274 23275 this.addClass(sPrefix + this.CSS_CLASS_NAME + ("-" + p_sState)); 23276 23277 } 23278 23279 this.addClass(sPrefix + sType + ("-button-" + p_sState)); 23280 23281 } 23282 23283 }, 23284 23285 23286 /** 23287 * @method removeStateCSSClasses 23288 * @description Removes state-specific CSS classes to the button's root 23289 * DOM element. 23290 */ 23291 removeStateCSSClasses: function (p_sState) { 23292 23293 var sType = this.get("type"), 23294 sPrefix = this.CLASS_NAME_PREFIX; 23295 23296 if (Lang.isString(p_sState)) { 23297 23298 this.removeClass(sPrefix + this.CSS_CLASS_NAME + ("-" + p_sState)); 23299 this.removeClass(sPrefix + sType + ("-button-" + p_sState)); 23300 23301 } 23302 23303 }, 23304 23305 23306 /** 23307 * @method createHiddenFields 23308 * @description Creates the button's hidden form field and appends it 23309 * to its parent form. 23310 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 23311 * level-one-html.html#ID-6043025">HTMLInputElement</a>|Array} 23312 */ 23313 createHiddenFields: function () { 23314 23315 this.removeHiddenFields(); 23316 23317 var oForm = this.getForm(), 23318 oButtonField, 23319 sType, 23320 bCheckable, 23321 oMenu, 23322 oMenuItem, 23323 sButtonName, 23324 oValue, 23325 oMenuField, 23326 oReturnVal, 23327 sMenuFieldName, 23328 oMenuSrcElement, 23329 bMenuSrcElementIsSelect = false; 23330 23331 23332 if (oForm && !this.get("disabled")) { 23333 23334 sType = this.get("type"); 23335 bCheckable = (sType == "checkbox" || sType == "radio"); 23336 23337 23338 if ((bCheckable && this.get("checked")) || (m_oSubmitTrigger == this)) { 23339 23340 YAHOO.log("Creating hidden field.", "info", this.toString()); 23341 23342 oButtonField = createInputElement((bCheckable ? sType : "hidden"), 23343 this.get("name"), this.get("value"), this.get("checked")); 23344 23345 23346 if (oButtonField) { 23347 23348 if (bCheckable) { 23349 23350 oButtonField.style.display = "none"; 23351 23352 } 23353 23354 oForm.appendChild(oButtonField); 23355 23356 } 23357 23358 } 23359 23360 23361 oMenu = this._menu; 23362 23363 23364 if (Menu && oMenu && (oMenu instanceof Menu)) { 23365 23366 YAHOO.log("Creating hidden field for menu.", "info", this.toString()); 23367 23368 oMenuItem = this.get("selectedMenuItem"); 23369 oMenuSrcElement = oMenu.srcElement; 23370 bMenuSrcElementIsSelect = (oMenuSrcElement && 23371 oMenuSrcElement.nodeName.toUpperCase() == "SELECT"); 23372 23373 if (oMenuItem) { 23374 23375 oValue = (oMenuItem.value === null || oMenuItem.value === "") ? 23376 oMenuItem.cfg.getProperty("text") : oMenuItem.value; 23377 23378 sButtonName = this.get("name"); 23379 23380 23381 if (bMenuSrcElementIsSelect) { 23382 23383 sMenuFieldName = oMenuSrcElement.name; 23384 23385 } 23386 else if (sButtonName) { 23387 23388 sMenuFieldName = (sButtonName + "_options"); 23389 23390 } 23391 23392 23393 if (oValue && sMenuFieldName) { 23394 23395 oMenuField = createInputElement("hidden", sMenuFieldName, oValue); 23396 oForm.appendChild(oMenuField); 23397 23398 } 23399 23400 } 23401 else if (bMenuSrcElementIsSelect) { 23402 23403 oMenuField = oForm.appendChild(oMenuSrcElement); 23404 23405 } 23406 23407 } 23408 23409 23410 if (oButtonField && oMenuField) { 23411 23412 this._hiddenFields = [oButtonField, oMenuField]; 23413 23414 } 23415 else if (!oButtonField && oMenuField) { 23416 23417 this._hiddenFields = oMenuField; 23418 23419 } 23420 else if (oButtonField && !oMenuField) { 23421 23422 this._hiddenFields = oButtonField; 23423 23424 } 23425 23426 oReturnVal = this._hiddenFields; 23427 23428 } 23429 23430 return oReturnVal; 23431 23432 }, 23433 23434 23435 /** 23436 * @method removeHiddenFields
23437 * @description Removes the button's hidden form field(s) from its 23438 * parent form. 23439 */ 23440 removeHiddenFields: function () { 23441 23442 var oField = this._hiddenFields, 23443 nFields, 23444 i; 23445 23446 function removeChild(p_oElement) { 23447 23448 if (Dom.inDocument(p_oElement)) { 23449 23450 p_oElement.parentNode.removeChild(p_oElement); 23451 23452 } 23453 23454 } 23455 23456 23457 if (oField) { 23458 23459 if (Lang.isArray(oField)) { 23460 23461 nFields = oField.length; 23462 23463 if (nFields > 0) { 23464 23465 i = nFields - 1; 23466 23467 do { 23468 23469 removeChild(oField[i]); 23470 23471 } 23472 while (i--); 23473 23474 } 23475 23476 } 23477 else { 23478 23479 removeChild(oField); 23480 23481 } 23482 23483 this._hiddenFields = null; 23484 23485 } 23486 23487 }, 23488 23489 23490 /** 23491 * @method submitForm 23492 * @description Submits the form to which the button belongs. Returns 23493 * true if the form was submitted successfully, false if the submission 23494 * was cancelled. 23495 * @protected 23496 * @return {Boolean} 23497 */ 23498 submitForm: function () { 23499 23500 var oForm = this.getForm(), 23501 23502 oSrcElement = this.get("srcelement"), 23503 23504 /* 23505 Boolean indicating if the event fired successfully 23506 (was not cancelled by any handlers) 23507 */ 23508
23509 bSubmitForm = false, 23510 23511 oEvent; 23512 23513 23514 if (oForm) { 23515 23516 if (this.get("type") == "submit" || (oSrcElement && oSrcElement.type == "submit")) { 23517 23518 m_oSubmitTrigger = this; 23519 23520 } 23521 23522 23523 if (UA.ie && (UA.ie < 9)) { 23524 23525 bSubmitForm = oForm.fireEvent("onsubmit"); 23526 23527 } 23528 else { // Gecko, Opera, and Safari 23529 23530 oEvent = document.createEvent("HTMLEvents"); 23531 oEvent.initEvent("submit", true, true); 23532 23533 bSubmitForm = oForm.dispatchEvent(oEvent); 23534 23535 } 23536 23537 23538 /* 23539 In IE and Safari, dispatching a "submit" event to a form 23540 WILL cause the form's "submit" event to fire, but WILL NOT 23541 submit the form. Therefore, we need to call the "submit" 23542 method as well. 23543 */ 23544 23545 if ((UA.ie || UA.webkit) && bSubmitForm) { 23546 23547 oForm.submit(); 23548 23549 } 23550 23551 } 23552 23553 return bSubmitForm; 23554 23555 }, 23556 23557 23558 /** 23559 * @method init 23560 * @description The Button class's initialization method. 23561 * @param {String} p_oElement String specifying the id attribute of the 23562 * <code><input></code>, <code><button></code>, 23563 * <code><a></code>, or <code><span></code> element to 23564 * be used to create the button. 23565 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 23566 * level-one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http:// 23567 * www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html 23568 * #ID-34812697">HTMLButtonElement</a>|<a href="http://www.w3.org/TR 23569 * /2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-33759296"> 23570 * HTMLElement</a>} p_oElement Object reference for the 23571 * <code><input></code>, <code><button></code>, 23572 * <code><a></code>, or <code><span></code> element to be 23573 * used to create the button. 23574 * @param {Object} p_oElement Object literal specifying a set of 23575 * configuration attributes used to create the button. 23576 * @param {Object} p_oAttributes Optional. Object literal specifying a 23577 * set of configuration attributes used to create the button. 23578 */ 23579 init: function (p_oElement, p_oAttributes) { 23580 23581 var sNodeName = p_oAttributes.type == "link" ? "a" : "button", 23582 oSrcElement = p_oAttributes.srcelement, 23583 oButton = p_oElement.getElementsByTagName(sNodeName)[0], 23584 oInput; 23585 23586 23587 if (!oButton) { 23588 23589 oInput = p_oElement.getElementsByTagName("input")[0]; 23590 23591 23592 if (oInput) { 23593 23594 oButton = document.createElement("button"); 23595 oButton.setAttribute("type", "button"); 23596 23597 oInput.parentNode.replaceChild(oButton, oInput); 23598 23599 } 23600 23601 } 23602 23603 this._button = oButton; 23604 23605 23606 YAHOO.widget.Button.superclass.init.call(this, p_oElement, p_oAttributes); 23607 23608 23609 var sId = this.get("id"), 23610 sButtonId = sId + "-button"; 23611 23612 23613 oButton.id = sButtonId; 23614 23615 23616 var aLabels, 23617 oLabel; 23618 23619 23620 var hasLabel = function (element) { 23621 23622 return (element.htmlFor === sId); 23623 23624 }; 23625 23626 23627 var setLabel = function () { 23628 23629 oLabel.setAttribute((UA.ie ? "htmlFor" : "for"), sButtonId); 23630 23631 }; 23632 23633 23634 if (oSrcElement && this.get("type") != "link") { 23635 23636 aLabels = Dom.getElementsBy(hasLabel, "label"); 23637 23638 if (Lang.isArray(aLabels) && aLabels.length > 0) { 23639 23640 oLabel = aLabels[0]; 23641 23642 } 23643 23644 } 23645 23646 23647 m_oButtons[sId] = this; 23648 23649 var sPrefix = this.CLASS_NAME_PREFIX; 23650 23651 this.addClass(sPrefix + this.CSS_CLASS_NAME); 23652 this.addClass(sPrefix + this.get("type") + "-button"); 23653 23654 Event.on(this._button, "focus", this._onFocus, null, this); 23655 this.on("mouseover", this._onMouseOver); 23656 this.on("mousedown", this._onMouseDown); 23657 this.on("mouseup", this._onMouseUp); 23658 this.on("click", this._onClick); 23659 23660 // Need to reset the value of the "onclick" Attribute so that any 23661 // handlers registered via the "onclick" Attribute are fired after 23662 // Button's default "_onClick" listener. 23663 23664 var fnOnClick = this.get("onclick"); 23665 23666 this.set("onclick", null); 23667 this.set("onclick", fnOnClick); 23668 23669 this.on("dblclick", this._onDblClick); 23670 23671 23672 var oParentNode; 23673 23674 if (oLabel) { 23675 23676 if (this.get("replaceLabel")) { 23677
23678 this.set("label", oLabel.innerHTML); 23679 23680 oParentNode = oLabel.parentNode; 23681 23682 oParentNode.removeChild(oLabel); 23683 23684 } 23685 else { 23686 23687 this.on("appendTo", setLabel); 23688 23689 Event.on(oLabel, "click", this._onLabelClick, null, this); 23690 23691 this._label = oLabel; 23692 23693 } 23694 23695 } 23696 23697 this.on("appendTo", this._onAppendTo); 23698 23699 23700 23701 var oContainer = this.get("container"), 23702 oElement = this.get("element"), 23703 bElInDoc = Dom.inDocument(oElement); 23704 23705 23706 if (oContainer) { 23707 23708 if (oSrcElement && oSrcElement != oElement) { 23709 23710 oParentNode = oSrcElement.parentNode; 23711 23712 if (oParentNode) { 23713 23714 oParentNode.removeChild(oSrcElement); 23715 23716 } 23717 23718 } 23719 23720 if (Lang.isString(oContainer)) { 23721 23722 Event.onContentReady(oContainer, this.appendTo, oContainer, this); 23723 23724 } 23725 else { 23726 23727 this.on("init", function () { 23728 23729 Lang.later(0, this, this.appendTo, oContainer); 23730 23731 }); 23732 23733 } 23734 23735 } 23736 else if (!bElInDoc && oSrcElement && oSrcElement != oElement) { 23737 23738 oParentNode = oSrcElement.parentNode; 23739 23740 if (oParentNode) { 23741 23742 this.fireEvent("beforeAppendTo", { 23743 type: "beforeAppendTo", 23744 target: oParentNode 23745 }); 23746 23747 oParentNode.replaceChild(oElement, oSrcElement); 23748 23749 this.fireEvent("appendTo", { 23750 type: "appendTo", 23751 target: oParentNode 23752 }); 23753 23754 } 23755 23756 } 23757 else if (this.get("type") != "link" && bElInDoc && oSrcElement && 23758 oSrcElement == oElement) { 23759 23760 this._addListenersToForm(); 23761 23762 } 23763 23764 YAHOO.log("Initialization completed.", "info", this.toString()); 23765 23766 23767 this.fireEvent("init", { 23768 type: "init", 23769 target: this 23770 }); 23771 23772 }, 23773 23774 23775 /** 23776 * @method initAttributes 23777 * @description Initializes all of the configuration attributes used to 23778 * create the button. 23779 * @param {Object} p_oAttributes Object literal specifying a set of 23780 * configuration attributes used to create the button. 23781 */ 23782 initAttributes: function (p_oAttributes) { 23783 23784 var oAttributes = p_oAttributes || {}; 23785 23786 YAHOO.widget.Button.superclass.initAttributes.call(this, 23787 oAttributes); 23788 23789 23790 /** 23791 * @attribute type 23792 * @description String specifying the button's type. Possible 23793 * values are: "push," "link," "submit," "reset," "checkbox," 23794 * "radio," "menu," and "split." 23795 * @default "push"
23796 * @type String 23797 * @writeonce 23798 */ 23799 this.setAttributeConfig("type", { 23800 23801 value: (oAttributes.type || "push"), 23802 validator: Lang.isString, 23803 writeOnce: true, 23804 method: this._setType 23805 23806 }); 23807 23808 23809 /** 23810 * @attribute label 23811 * @description {HTML} specifying the button's text label 23812 * or innerHTML. 23813 * @default null 23814 * @type String 23815 */ 23816 this.setAttributeConfig("label", { 23817 23818 value: oAttributes.label, 23819 validator: Lang.isString, 23820 method: this._setLabel 23821 23822 }); 23823 23824 23825 /** 23826 * @attribute value 23827 * @description Object specifying the value for the button. 23828 * @default null 23829 * @type Object 23830 */ 23831 this.setAttributeConfig("value", { 23832 23833 value: oAttributes.value 23834 23835 }); 23836 23837 23838 /** 23839 * @attribute name 23840 * @description String specifying the name for the button. 23841 * @default null
23842 * @type String 23843 */ 23844 this.setAttributeConfig("name", { 23845 23846 value: oAttributes.name, 23847 validator: Lang.isString 23848 23849 }); 23850 23851 23852 /** 23853 * @attribute tabindex 23854 * @description Number specifying the tabindex for the button. 23855 * @default null 23856 * @type Number 23857 */ 23858 this.setAttributeConfig("tabindex", { 23859 23860 value: oAttributes.tabindex, 23861 validator: Lang.isNumber, 23862 method: this._setTabIndex 23863 23864 }); 23865 23866 23867 /** 23868 * @attribute title 23869 * @description String specifying the title for the button. 23870 * @default null 23871 * @type String 23872 */ 23873 this.configureAttribute("title", { 23874 23875 value: oAttributes.title, 23876 validator: Lang.isString, 23877 method: this._setTitle 23878 23879 }); 23880 23881 23882 /** 23883 * @attribute disabled 23884 * @description Boolean indicating if the button should be disabled. 23885 * (Disabled buttons are dimmed and will not respond to user input 23886 * or fire events. Does not apply to button's of type "link.") 23887 * @default false 23888 * @type Boolean 23889 */ 23890 this.setAttributeConfig("disabled", { 23891 23892 value: (oAttributes.disabled || false), 23893 validator: Lang.isBoolean, 23894 method: this._setDisabled 23895 23896 }); 23897 23898 23899 /** 23900 * @attribute href 23901 * @description String specifying the href for the button. Applies 23902 * only to buttons of type "link."
23903 * @type String 23904 */ 23905 this.setAttributeConfig("href", { 23906 23907 value: oAttributes.href, 23908 validator: Lang.isString, 23909 method: this._setHref 23910 23911 }); 23912 23913 23914 /** 23915 * @attribute target 23916 * @description String specifying the target for the button. 23917 * Applies only to buttons of type "link." 23918 * @type String 23919 */ 23920 this.setAttributeConfig("target", { 23921 23922 value: oAttributes.target, 23923 validator: Lang.isString, 23924 method: this._setTarget 23925 23926 }); 23927 23928 23929 /** 23930 * @attribute checked 23931 * @description Boolean indicating if the button is checked. 23932 * Applies only to buttons of type "radio" and "checkbox." 23933 * @default false 23934 * @type Boolean 23935 */ 23936 this.setAttributeConfig("checked", { 23937 23938 value: (oAttributes.checked || false), 23939 validator: Lang.isBoolean, 23940 method: this._setChecked 23941 23942 }); 23943 23944 23945 /** 23946 * @attribute container 23947 * @description HTML element reference or string specifying the id 23948 * attribute of the HTML element that the button's markup should be 23949 * rendered into. 23950 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 23951 * level-one-html.html#ID-58190037">HTMLElement</a>|String 23952 * @default null 23953 * @writeonce 23954 */ 23955 this.setAttributeConfig("container", { 23956 23957 value: oAttributes.container, 23958 writeOnce: true 23959 23960 }); 23961 23962 23963 /** 23964 * @attribute srcelement 23965 * @description Object reference to the HTML element (either 23966 * <code><input></code> or <code><span></code>) 23967 * used to create the button. 23968 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 23969 * level-one-html.html#ID-58190037">HTMLElement</a>|String 23970 * @default null 23971 * @writeonce 23972 */ 23973 this.setAttributeConfig("srcelement", { 23974 23975 value: oAttributes.srcelement, 23976 writeOnce: true 23977 23978 }); 23979 23980 23981 /** 23982 * @attribute menu 23983 * @description Object specifying the menu for the button. 23984 * The value can be one of the following: 23985 * <ul> 23986 * <li>Object specifying a rendered <a href="YAHOO.widget.Menu.html"> 23987 * YAHOO.widget.Menu</a> instance.</li> 23988 * <li>Object specifying a rendered <a href="YAHOO.widget.Overlay.html"> 23989 * YAHOO.widget.Overlay</a> instance.</li> 23990 * <li>String specifying the id attribute of the <code><div> 23991 * </code> element used to create the menu. By default the menu 23992 * will be created as an instance of 23993 * <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>. 23994 * If the <a href="YAHOO.widget.Menu.html#CSS_CLASS_NAME"> 23995 * default CSS class name for YAHOO.widget.Menu</a> is applied to 23996 * the <code><div></code> element, it will be created as an 23997 * instance of <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu 23998 * </a>.</li><li>String specifying the id attribute of the 23999 * <code><select></code> element used to create the menu. 24000 * </li><li>Object specifying the <code><div></code> element 24001 * used to create the menu.</li> 24002 * <li>Object specifying the <code><select></code> element 24003 * used to create the menu.</li> 24004 * <li>Array of object literals, each representing a set of 24005 * <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a> 24006 * configuration attributes.</li> 24007 * <li>
24007Array of strings representing the text labels for each menu 24008 * item in the menu.</li> 24009 * </ul> 24010 * @type <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>|<a 24011 * href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>|<a 24012 * href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 24013 * one-html.html#ID-58190037">HTMLElement</a>|String|Array 24014 * @default null 24015 * @writeonce 24016 */ 24017 this.setAttributeConfig("menu", { 24018 24019 value: null, 24020 method: this._setMenu, 24021 writeOnce: true 24022 24023 }); 24024 24025 24026 /** 24027 * @attribute lazyloadmenu 24028 * @description Boolean indicating the value to set for the 24029 * <a href="YAHOO.widget.Menu.html#lazyLoad">"lazyload"</a> 24030 * configuration property of the button's menu. Setting 24031 * "lazyloadmenu" to <code>true </code> will defer rendering of 24032 * the button's menu until the first time it is made visible. 24033 * If "lazyloadmenu" is set to <code>false</code>, the button's 24034 * menu will be rendered immediately if the button is in the 24035 * document, or in response to the button's "appendTo" event if 24036 * the button is not yet in the document. In either case, the 24037 * menu is rendered into the button's parent HTML element. 24038 * <em>This attribute does not apply if a 24039 * <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a> or 24040 * <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a> 24041 * instance is passed as the value of the button's "menu" 24042 * configuration attribute. <a href="YAHOO.widget.Menu.html"> 24043 * YAHOO.widget.Menu</a> or <a href="YAHOO.widget.Overlay.html"> 24044 * YAHOO.widget.Overlay</a> instances should be rendered before 24045 * being set as the value for the "menu" configuration 24046 * attribute.</em> 24047 * @default true 24048 * @type Boolean 24049 * @writeonce 24050 */ 24051 this.setAttributeConfig("lazyloadmenu", { 24052 24053 value: (oAttributes.lazyloadmenu === false ? false : true), 24054 validator: Lang.isBoolean, 24055 writeOnce: true 24056 24057 }); 24058 24059 24060 /** 24061 * @attribute menuclassname 24062 * @description String representing the CSS class name to be 24063 * applied to the root element of the button's menu.
24064 * @type String 24065 * @default "yui-button-menu" 24066 * @writeonce 24067 */ 24068 this.setAttributeConfig("menuclassname", { 24069 24070 value: (oAttributes.menuclassname || (this.CLASS_NAME_PREFIX + "button-menu")), 24071 validator: Lang.isString, 24072 method: this._setMenuClassName, 24073 writeOnce: true 24074 24075 }); 24076 24077 24078 /** 24079 * @attribute menuminscrollheight 24080 * @description Number defining the minimum threshold for the "menumaxheight" 24081 * configuration attribute. When set this attribute is automatically applied 24082 * to all submenus. 24083 * @default 90 24084 * @type Number 24085 */ 24086 this.setAttributeConfig("menuminscrollheight", { 24087 24088 value: (oAttributes.menuminscrollheight || 90), 24089 validator: Lang.isNumber 24090 24091 }); 24092 24093 24094 /** 24095 * @attribute menumaxheight 24096 * @description Number defining the maximum height (in pixels) for a menu's 24097 * body element (<code><div class="bd"<</code>). Once a menu's body 24098 * exceeds this height, the contents of the body are scrolled to maintain 24099 * this value. This value cannot be set lower than the value of the 24100 * "minscrollheight" configuration property. 24101 * @type Number 24102 * @default 0 24103 */ 24104 this.setAttributeConfig("menumaxheight", { 24105 24106 value: (oAttributes.menumaxheight || 0), 24107 validator: Lang.isNumber 24108 24109 }); 24110 24111 24112 /** 24113 * @attribute menualignment 24114 * @description Array defining how the Button's Menu is aligned to the Button. 24115 * The default value of ["tl", "bl"] aligns the Menu's top left corner to the Button's 24116 * bottom left corner. 24117 * @type Array 24118 * @default ["tl", "bl"] 24119 */ 24120 this.setAttributeConfig("menualignment", { 24121 24122 value: (oAttributes.menualignment || ["tl", "bl"]), 24123 validator: Lang.isArray 24124 24125 }); 24126 24127 24128 /** 24129 * @attribute selectedMenuItem 24130 * @description Object representing the item in the button's menu 24131 * that is currently selected. 24132 * @type YAHOO.widget.MenuItem 24133 * @default null 24134 */ 24135 this.setAttributeConfig("selectedMenuItem", { 24136 24137 value: null 24138 24139 }); 24140 24141 24142 /** 24143 * @attribute onclick 24144 * @description Object literal representing the code to be executed 24145 * when the button is clicked. Format:<br> <code> {<br> 24146 * <strong>fn:</strong> Function, // The handler to call 24147 * when the event fires.<br> <strong>obj:</strong> Object, 24148 * // An object to pass back to the handler.<br> 24149 * <strong>scope:</strong> Object // The object to use 24150 * for the scope of the handler.<br> } </code> 24151 * @type Object 24152 * @default null 24153 */ 24154 this.setAttributeConfig("onclick", { 24155 24156 value: oAttributes.onclick, 24157 method: this._setOnClick 24158 24159 }); 24160 24161 24162 /** 24163 * @attribute focusmenu 24164 * @description Boolean indicating whether or not the button's menu 24165 * should be focused when it is made visible. 24166 * @type Boolean 24167 * @default true 24168 */ 24169 this.setAttributeConfig("focusmenu", { 24170 24171 value: (oAttributes.focusmenu === false ? false : true), 24172 validator: Lang.isBoolean 24173 24174 }); 24175 24176 24177 /** 24178 * @attribute replaceLabel 24179 * @description Boolean indicating whether or not the text of the 24180 * button's <code><label></code> element should be used as 24181 * the source for the button's label configuration attribute and 24182 * removed from the DOM. 24183 * @type Boolean 24184 * @default false 24185 */ 24186 this.setAttributeConfig("replaceLabel", { 24187 24188 value: false, 24189 validator: Lang.isBoolean, 24190 writeOnce: true 24191 24192 }); 24193 24194 }, 24195 24196 24197 /**
24198 * @method focus 24199 * @description Causes the button to receive the focus and fires the 24200 * button's "focus" event. 24201 */ 24202 focus: function () { 24203 24204 if (!this.get("disabled")) { 24205 //Adding a try/catch in case the element is not 24206 // visible by the time it's focus is being called. 24207 // for example, on a dialog that closes on button click 24208 try { 24209 this._button.focus(); 24210 } catch (e) {} 24211 24212 } 24213 24214 }, 24215 24216 24217 /** 24218 * @method blur 24219 * @description Causes the button to lose focus and fires the button's 24220 * "blur" event. 24221 */ 24222 blur: function () { 24223 24224 if (!this.get("disabled")) { 24225 //Adding a try/catch in case the element is not 24226 // visible by the time it's focus is being called. 24227 // for example, on a dialog that closes on button click 24228 try { 24229 this._button.blur(); 24230 } catch (e) {} 24231 24232 } 24233 24234 }, 24235 24236 24237 /** 24238 * @method hasFocus 24239 * @description Returns a boolean indicating whether or not the button 24240 * has focus. 24241 * @return {Boolean} 24242 */ 24243 hasFocus: function () { 24244 24245 return (m_oFocusedButton == this); 24246 24247 }, 24248 24249 24250 /** 24251 * @method isActive 24252 * @description Returns a boolean indicating whether or not the button 24253 * is active. 24254 * @return {Boolean} 24255 */ 24256 isActive: function () { 24257 24258 return this.hasClass(this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME + "-active"); 24259 24260 }, 24261 24262 24263 /** 24264 * @method getMenu 24265 * @description Returns a reference to the button's menu. 24266 * @return {<a href="YAHOO.widget.Overlay.html"> 24267 * YAHOO.widget.Overlay</a>|<a 24268 * href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>} 24269 */ 24270 getMenu: function () { 24271 24272 return this._menu; 24273 24274 }, 24275 24276 24277 /** 24278 * @method getForm 24279 * @description Returns a reference to the button's parent form. 24280 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1- 24281 * 20000929/level-one-html.html#ID-40002357">HTMLFormElement</a>} 24282 */ 24283 getForm: function () { 24284 24285 var oButton = this._button, 24286 oForm; 24287 24288 if (oButton) { 24289 24290 oForm = oButton.form; 24291 24292 } 24293 24294 return oForm; 24295 24296 }, 24297 24298 24299 /** 24300 * @method getHiddenFields 24301 * @description Returns an <code><input></code> element or 24302 * array of form elements used to represent the button when its parent 24303 * form is submitted. 24304 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 24305 * level-one-html.html#ID-6043025">HTMLInputElement</a>|Array} 24306 */ 24307 getHiddenFields: function () { 24308 24309 return this._hiddenFields; 24310 24311 }, 24312 24313 24314 /** 24315 * @method destroy 24316 * @description Removes the button's element from its parent element and 24317 * removes all event handlers. 24318 */ 24319 destroy: function () { 24320 24321 YAHOO.log("Destroying ...", "info", this.toString()); 24322 24323 var oElement = this.get("element"), 24324 oMenu = this._menu, 24325 oLabel = this._label, 24326 oParentNode, 24327 aButtons; 24328 24329 if (oMenu) { 24330 24331 YAHOO.log("Destroying menu.", "info", this.toString()); 24332 24333 if (m_oOverlayManager && m_oOverlayManager.find(oMenu)) { 24334 24335 m_oOverlayManager.remove(oMenu); 24336 24337 } 24338 24339 oMenu.destroy(); 24340 24341 } 24342 24343 YAHOO.log("Removing DOM event listeners.", "info", this.toString()); 24344 24345 Event.purgeElement(oElement); 24346 Event.purgeElement(this._button); 24347 Event.removeListener(document, "mouseup", this._onDocumentMouseUp); 24348 Event.removeListener(document, "keyup", this._onDocumentKeyUp); 24349 Event.removeListener(document, "mousedown", this._onDocumentMouseDown); 24350 24351 24352 if (oLabel) { 24353 24354 Event.removeListener(oLabel, "click", this._onLabelClick); 24355 24356 oParentNode = oLabel.parentNode; 24357 oParentNode.removeChild(oLabel); 24358 24359 } 24360 24361 24362 var oForm = this.getForm(); 24363 24364 if (oForm) { 24365 24366 Event.removeListener(oForm, "reset", this._onFormReset); 24367 Event.removeListener(oForm, "submit", this._onFormSubmit); 24368 24369 } 24370 24371 YAHOO.log("Removing CustomEvent listeners.", "info", this.toString()); 24372 24373 this.unsubscribeAll(); 24374 24375 oParentNode = oElement.parentNode; 24376 24377 if (oParentNode) { 24378 24379 oParentNode.removeChild(oElement); 24380 24381 } 24382 24383 YAHOO.log("Removing from document.", "info", this.toString()); 24384 24385 delete m_oButtons[this.get("id")]; 24386 24387 var sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME); 24388 24389 aButtons = Dom.getElementsByClassName(sClass, 24390 this.NODE_NAME, oForm); 24391 24392 if (Lang.isArray(aButtons) && aButtons.length === 0) { 24393 24394 Event.removeListener(oForm, "keypress", 24395 YAHOO.widget.Button.onFormKeyPress); 24396 24397 } 24398 24399 YAHOO.log("Destroyed.", "info", this.toString()); 24400 24401 }, 24402 24403 24404 fireEvent: function (p_sType , p_aArgs) { 24405 24406 var sType = arguments[0]; 24407 24408 // Disabled buttons should not respond to DOM events 24409 24410 if (this.DOM_EVENTS[sType] && this.get("disabled")) { 24411 24412 return false;
24413 24414 } 24415 24416 return YAHOO.widget.Button.superclass.fireEvent.apply(this, arguments); 24417 24418 }, 24419 24420 24421 /** 24422 * @method toString 24423 * @description Returns a string representing the button. 24424 * @return {String} 24425 */ 24426 toString: function () { 24427 24428 return ("Button " + this.get("id")); 24429 24430 } 24431 24432 }); 24433 24434 24435 /** 24436 * @method YAHOO.widget.Button.onFormKeyPress 24437 * @description "keypress" event handler for the button's form. 24438 * @param {Event} p_oEvent Object representing the DOM event object passed 24439 * back by the event utility (YAHOO.util.Event). 24440 */ 24441 YAHOO.widget.Button.onFormKeyPress = function (p_oEvent) { 24442 24443 var oTarget = Event.getTarget(p_oEvent), 24444 nCharCode = Event.getCharCode(p_oEvent), 24445 sNodeName = oTarget.nodeName && oTarget.nodeName.toUpperCase(), 24446 sType = oTarget.type, 24447 24448 /* 24449 Boolean indicating if the form contains any enabled or 24450 disabled YUI submit buttons 24451 */ 24452 24453 bFormContainsYUIButtons = false, 24454 24455 oButton, 24456 24457 oYUISubmitButton, // The form's first, enabled YUI submit button 24458 24459 /* 24460 The form's first, enabled HTML submit button that precedes any 24461 YUI submit button 24462 */ 24463 24464 oPrecedingSubmitButton, 24465 24466 oEvent; 24467 24468 24469 function isSubmitButton(p_oElement) { 24470 24471 var sId, 24472 oSrcElement; 24473 24474 switch (p_oElement.nodeName.toUpperCase()) { 24475 24476 case "INPUT": 24477 case "BUTTON": 24478 24479 if (p_oElement.type == "submit" && !p_oElement.disabled) { 24480 24481 if (!bFormContainsYUIButtons && !oPrecedingSubmitButton) { 24482 24483 oPrecedingSubmitButton = p_oElement; 24484 24485 } 24486 24487 } 24488 24489 break; 24490 24491 24492 default: 24493 24494 sId = p_oElement.id; 24495 24496 if (sId) { 24497 24498 oButton = m_oButtons[sId]; 24499 24500 if (oButton) { 24501 24502 bFormContainsYUIButtons = true; 24503 24504 if (!oButton.get("disabled")) { 24505 24506 oSrcElement = oButton.get("srcelement"); 24507 24508 if (!oYUISubmitButton && (oButton.get("type") == "submit" || 24509 (oSrcElement && oSrcElement.type == "submit"))) { 24510 24511 oYUISubmitButton = oButton; 24512 24513 } 24514 24515 } 24516 24517 } 24518 24519 } 24520 24521 break; 24522 24523 } 24524 24525 } 24526 24527 24528 if (nCharCode == 13 && ((sNodeName == "INPUT" && (sType == "text" || 24529 sType == "password" || sType == "checkbox" || sType == "radio" || 24530 sType == "file")) || sNodeName == "SELECT")) { 24531 24532 Dom.getElementsBy(isSubmitButton, "*", this); 24533 24534 24535 if (oPrecedingSubmitButton) { 24536 24537 /* 24538 Need to set focus to the first enabled submit button 24539 to make sure that IE includes its name and value 24540 in the form's data set. 24541 */ 24542 24543 oPrecedingSubmitButton.focus(); 24544 24545 } 24546 else if (!oPrecedingSubmitButton && oYUISubmitButton) { 24547 24548 /* 24549 Need to call "preventDefault" to ensure that the form doesn't end up getting 24550 submitted twice. 24551 */ 24552 24553 Event.preventDefault(p_oEvent); 24554 24555 24556 if (UA.ie && (UA.ie < 9)) { 24557 24558 oYUISubmitButton.get("element").fireEvent("onclick"); 24559 24560 } 24561 else { 24562 24563 oEvent = document.createEvent("HTMLEvents"); 24564 oEvent.initEvent("click", true, true); 24565 24566 24567 if (UA.gecko < 1.9) { 24568 24569 oYUISubmitButton.fireEvent("click", oEvent); 24570 24571 } 24572 else { 24573 24574 oYUISubmitButton.get("element").dispatchEvent(oEvent); 24575 24576 } 24577 24578 } 24579 24580 } 24581 24582 } 24583 24584 }; 24585 24586 24587 /** 24588 * @method YAHOO.widget.Button.addHiddenFieldsToForm
24589 * @description Searches the specified form and adds hidden fields for 24590 * instances of YAHOO.widget.Button that are of type "radio," "checkbox," 24591 * "menu," and "split." 24592 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 24593 * one-html.html#ID-40002357">HTMLFormElement</a>} p_oForm Object reference 24594 * for the form to search. 24595 */ 24596 YAHOO.widget.Button.addHiddenFieldsToForm = function (p_oForm) { 24597 24598 var proto = YAHOO.widget.Button.prototype, 24599 aButtons = Dom.getElementsByClassName( 24600 (proto.CLASS_NAME_PREFIX + proto.CSS_CLASS_NAME), 24601 "*", 24602 p_oForm), 24603 24604 nButtons = aButtons.length, 24605 oButton, 24606 sId, 24607 i; 24608 24609 if (nButtons > 0) { 24610 24611 YAHOO.log("Form contains " + nButtons + " YUI buttons.", "info", this.toString()); 24612 24613 for (i = 0; i < nButtons; i++) { 24614 24615 sId = aButtons[i].id; 24616 24617 if (sId) { 24618 24619 oButton = m_oButtons[sId]; 24620 24621 if (oButton) { 24622 24623 oButton.createHiddenFields(); 24624 24625 } 24626 24627 } 24628 24629 } 24630 24631 } 24632 24633 }; 24634 24635 24636 /** 24637 * @method YAHOO.widget.Button.getButton 24638 * @description Returns a button with the specified id. 24639 * @param {String} p_sId String specifying the id of the root node of the 24640 * HTML element representing the button to be retrieved. 24641 * @return {YAHOO.widget.Button} 24642 */ 24643 YAHOO.widget.Button.getButton = function (p_sId) { 24644 24645 return m_oButtons[p_sId]; 24646 24647 }; 24648 24649 24650 // Events 24651 24652 24653 /** 24654 * @event focus 24655 * @description Fires when the menu item receives focus. Passes back a 24656 * single object representing the original DOM event object passed back by 24657 * the event utility (YAHOO.util.Event) when the event was fired. See 24658 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 24659 * for more information on listening for this event. 24660 * @type YAHOO.util.CustomEvent 24661 */ 24662 24663 24664 /** 24665 * @event blur 24666 * @description Fires when the menu item loses the input focus. Passes back 24667 * a single object representing the original DOM event object passed back by 24668 * the event utility (YAHOO.util.Event) when the event was fired. See 24669 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for 24670 * more information on listening for this event. 24671 * @type YAHOO.util.CustomEvent 24672 */ 24673 24674 24675 /** 24676 * @event option 24677 * @description Fires when the user invokes the button's option. Passes 24678 * back a single object representing the original DOM event (either 24679 * "mousedown" or "keydown") that caused the "option" event to fire. See 24680 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 24681 * for more information on listening for this event. 24682 * @type YAHOO.util.CustomEvent 24683 */ 24684 24685})(); 24686(function () { 24687 24688 // Shorthard for utilities 24689 24690 var Dom = YAHOO.util.Dom, 24691 Event = YAHOO.util.Event, 24692 Lang = YAHOO.lang, 24693 Button = YAHOO.widget.Button, 24694 24695 // Private collection of radio buttons 24696 24697 m_oButtons = {}; 24698 24699 24700 24701 /** 24702 * The ButtonGroup class creates a set of buttons that are mutually 24703 * exclusive; checking one button in the set will uncheck all others in the 24704 * button group. 24705 * @param {String} p_oElement String specifying the id attribute of the 24706 * <code><div></code> element of the button group. 24707 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 24708 * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 24709 * specifying the <code><div></code> element of the button group. 24710 * @param {Object} p_oElement Object literal specifying a set of 24711 * configuration attributes used to create the button group. 24712 * @param {Object} p_oAttributes Optional. Object literal specifying a set 24713 * of configuration attributes used to create the button group. 24714 * @namespace YAHOO.widget 24715 * @class ButtonGroup 24716 * @constructor 24717 * @extends YAHOO.util.Element 24718 */ 24719 YAHOO.widget.ButtonGroup = function (p_oElement, p_oAttributes) { 24720 24721 var fnSuperClass = YAHOO.widget.ButtonGroup.superclass.constructor, 24722 sNodeName, 24723 oElement, 24724 sId; 24725 24726 if (arguments.length == 1 && !Lang.isString(p_oElement) && 24727 !p_oElement.nodeName) { 24728 24729 if (!p_oElement.id) { 24730 24731 sId = Dom.generateId(); 24732 24733 p_oElement.id = sId; 24734 24735 YAHOO.log("No value specified for the button group's \"id\"" + 24736 " attribute. Setting button group id to \"" + sId + "\".", 24737 "info"); 24738 24739 } 24740 24741 this.logger = new YAHOO.widget.LogWriter("ButtonGroup " + sId); 24742 24743 this.logger.log("No source HTML element. Building the button " + 24744 "group using the set of configuration attributes."); 24745 24746 fnSuperClass.call(this, (this._createGroupElement()), p_oElement); 24747 24748 } 24749 else if (Lang.isString(p_oElement)) { 24750 24751 oElement = Dom.get(p_oElement); 24752 24753 if (oElement) { 24754 24755 if (oElement.nodeName.toUpperCase() == this.NODE_NAME) { 24756 24757 this.logger = 24758 new YAHOO.widget.LogWriter("ButtonGroup " + p_oElement); 24759 24760 fnSuperClass.call(this, oElement, p_oAttributes); 24761 24762 } 24763 24764 } 24765 24766 } 24767 else { 24768 24769 sNodeName = p_oElement.nodeName.toUpperCase(); 24770 24771 if (sNodeName && sNodeName == this.NODE_NAME) { 24772 24773 if (!p_oElement.id) { 24774 24775 p_oElement.id = Dom.generateId(); 24776 24777 YAHOO.log("No value specified for the button group's" + 24778 " \"id\" attribute. Setting button group id " + 24779 "to \"" + p_oElement.id + "\".", "warn"); 24780 24781 } 24782 24783 this.logger = 24784 new YAHOO.widget.LogWriter("ButtonGroup " + p_oElement.id); 24785 24786 fnSuperClass.call(this, p_oElement, p_oAttributes); 24787 24788 } 24789 24790 } 24791 24792 }; 24793 24794 24795 YAHOO.extend(YAHOO.widget.ButtonGroup, YAHOO.util.Element, { 24796 24797 24798 // Protected properties 24799 24800 24801 /** 24802 * @property _buttons
24803 * @description Array of buttons in the button group. 24804 * @default null 24805 * @protected 24806 * @type Array 24807 */ 24808 _buttons: null, 24809 24810 24811 24812 // Constants 24813 24814 24815 /** 24816 * @property NODE_NAME 24817 * @description The name of the tag to be used for the button 24818 * group's element. 24819 * @default "DIV" 24820 * @final 24821 * @type String 24822 */ 24823 NODE_NAME: "DIV", 24824 24825 24826 /** 24827 * @property CLASS_NAME_PREFIX 24828 * @description Prefix used for all class names applied to a ButtonGroup. 24829 * @default "yui-" 24830 * @final 24831 * @type String 24832 */ 24833 CLASS_NAME_PREFIX: "yui-", 24834 24835 24836 /** 24837 * @property CSS_CLASS_NAME 24838 * @description String representing the CSS class(es) to be applied 24839 * to the button group's element. 24840 * @default "buttongroup" 24841 * @final 24842 * @type String 24843 */ 24844 CSS_CLASS_NAME: "buttongroup", 24845 24846 24847 24848 // Protected methods 24849 24850 24851 /** 24852 * @method _createGroupElement 24853 * @description Creates the button group's element. 24854 * @protected 24855 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 24856 * level-one-html.html#ID-22445964">HTMLDivElement</a>} 24857 */ 24858 _createGroupElement: function () { 24859 24860 var oElement = document.createElement(this.NODE_NAME); 24861 24862 return oElement; 24863 24864 }, 24865 24866 24867 24868 // Protected attribute setter methods 24869 24870 24871 /** 24872 * @method _setDisabled 24873 * @description Sets the value of the button groups's 24874 * "disabled" attribute. 24875 * @protected 24876 * @param {Boolean} p_bDisabled Boolean indicating the value for 24877 * the button group's "disabled" attribute. 24878 */ 24879 _setDisabled: function (p_bDisabled) { 24880 24881 var nButtons = this.getCount(), 24882 i; 24883 24884 if (nButtons > 0) { 24885 24886 i = nButtons - 1; 24887 24888 do { 24889 24890 this._buttons[i].set("disabled", p_bDisabled); 24891 24892 } 24893 while (i--); 24894 24895 } 24896 24897 }, 24898 24899 24900 24901 // Protected event handlers 24902 24903 24904 /** 24905 * @method _onKeyDown 24906 * @description "keydown" event handler for the button group. 24907 * @protected 24908 * @param {Event} p_oEvent Object representing the DOM event object 24909 * passed back by the event utility (YAHOO.util.Event). 24910 */ 24911 _onKeyDown: function (p_oEvent) { 24912 24913 var oTarget = Event.getTarget(p_oEvent), 24914 nCharCode = Event.getCharCode(p_oEvent), 24915 sId = oTarget.parentNode.parentNode.id, 24916 oButton = m_oButtons[sId], 24917 nIndex = -1; 24918 24919 24920 if (nCharCode == 37 || nCharCode == 38) { 24921 24922 nIndex = (oButton.index === 0) ? 24923 (this._buttons.length - 1) : (oButton.index - 1); 24924 24925 } 24926 else if (nCharCode == 39 || nCharCode == 40) { 24927 24928 nIndex = (oButton.index === (this._buttons.length - 1)) ? 24929 0 : (oButton.index + 1); 24930 24931 } 24932 24933 24934 if (nIndex > -1) { 24935 24936 this.check(nIndex); 24937 this.getButton(nIndex).focus(); 24938 24939 } 24940 24941 }, 24942 24943 24944 /** 24945 * @method _onAppendTo 24946 * @description "appendTo" event handler for the button group. 24947 * @protected 24948 * @param {Event} p_oEvent Object representing the event that was fired. 24949 */ 24950 _onAppendTo: function (p_oEvent) { 24951 24952 var aButtons = this._buttons, 24953 nButtons = aButtons.length, 24954 i; 24955 24956 for (i = 0; i < nButtons; i++) { 24957 24958 aButtons[i].appendTo(this.get("element")); 24959 24960 } 24961 24962 }, 24963 24964 24965 /** 24966 * @method _onButtonCheckedChange
24967 * @description "checkedChange" event handler for each button in the 24968 * button group. 24969 * @protected 24970 * @param {Event} p_oEvent Object representing the event that was fired. 24971 * @param {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 24972 * p_oButton Object representing the button that fired the event. 24973 */ 24974 _onButtonCheckedChange: function (p_oEvent, p_oButton) { 24975 24976 var bChecked = p_oEvent.newValue, 24977 oCheckedButton = this.get("checkedButton"); 24978 24979 if (bChecked && oCheckedButton != p_oButton) { 24980 24981 if (oCheckedButton) { 24982 24983 oCheckedButton.set("checked", false, true); 24984 24985 } 24986 24987 this.set("checkedButton", p_oButton); 24988 this.set("value", p_oButton.get("value")); 24989 24990 } 24991 else if (oCheckedButton && !oCheckedButton.set("checked")) { 24992 24993 oCheckedButton.set("checked", true, true); 24994 24995 } 24996 24997 }, 24998 24999 25000 25001 // Public methods 25002 25003 25004 /** 25005 * @method init 25006 * @description The ButtonGroup class's initialization method. 25007 * @param {String} p_oElement String specifying the id attribute of the 25008 * <code><div></code> element of the button group. 25009 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 25010 * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 25011 * specifying the <code><div></code> element of the button group. 25012 * @param {Object} p_oElement Object literal specifying a set of 25013 * configuration attributes used to create the button group. 25014 * @param {Object} p_oAttributes Optional. Object literal specifying a 25015 * set of configuration attributes used to create the button group. 25016 */ 25017 init: function (p_oElement, p_oAttributes) { 25018 25019 this._buttons = []; 25020 25021 YAHOO.widget.ButtonGroup.superclass.init.call(this, p_oElement, 25022 p_oAttributes); 25023 25024 this.addClass(this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME); 25025 25026 25027 var sClass = (YAHOO.widget.Button.prototype.CLASS_NAME_PREFIX + "radio-button"), 25028 aButtons = this.getElementsByClassName(sClass); 25029 25030 this.logger.log("Searching for child nodes with the class name " + 25031 sClass + " to add to the button group."); 25032 25033 25034 if (aButtons.length > 0) { 25035 25036 this.logger.log("Found " + aButtons.length + 25037 " child nodes with the class name " + sClass + 25038 " Attempting to add to button group."); 25039 25040 this.addButtons(aButtons); 25041 25042 } 25043 25044 25045 this.logger.log("Searching for child nodes with the type of " + 25046 " \"radio\" to add to the button group."); 25047 25048 function isRadioButton(p_oElement) { 25049 25050 return (p_oElement.type == "radio"); 25051 25052 } 25053 25054 aButtons = 25055 Dom.getElementsBy(isRadioButton, "input", this.get("element")); 25056 25057 25058 if (aButtons.length > 0) { 25059 25060 this.logger.log("Found " + aButtons.length + " child nodes" + 25061 " with the type of \"radio.\" Attempting to add to" + 25062 " button group."); 25063 25064 this.addButtons(aButtons); 25065 25066 } 25067 25068 this.on("keydown", this._onKeyDown); 25069 this.on("appendTo", this._onAppendTo); 25070 25071 25072 var oContainer = this.get("container"); 25073 25074 if (oContainer) { 25075 25076 if (Lang.isString(oContainer)) { 25077 25078 Event.onContentReady(oContainer, function () { 25079 25080 this.appendTo(oContainer); 25081 25082 }, null, this); 25083 25084 } 25085 else { 25086 25087 this.appendTo(oContainer); 25088 25089 } 25090 25091 } 25092 25093 25094 this.logger.log("Initialization completed."); 25095 25096 }, 25097 25098 25099 /** 25100 * @method initAttributes
25101 * @description Initializes all of the configuration attributes used to 25102 * create the button group. 25103 * @param {Object} p_oAttributes Object literal specifying a set of 25104 * configuration attributes used to create the button group. 25105 */ 25106 initAttributes: function (p_oAttributes) { 25107 25108 var oAttributes = p_oAttributes || {}; 25109 25110 YAHOO.widget.ButtonGroup.superclass.initAttributes.call( 25111 this, oAttributes); 25112 25113 25114 /** 25115 * @attribute name 25116 * @description String specifying the name for the button group. 25117 * This name will be applied to each button in the button group. 25118 * @default null 25119 * @type String 25120 */ 25121 this.setAttributeConfig("name", { 25122 25123 value: oAttributes.name, 25124 validator: Lang.isString 25125 25126 }); 25127 25128 25129 /** 25130 * @attribute disabled 25131 * @description Boolean indicating if the button group should be 25132 * disabled. Disabling the button group will disable each button 25133 * in the button group. Disabled buttons are dimmed and will not 25134 * respond to user input or fire events. 25135 * @default false 25136 * @type Boolean 25137 */ 25138 this.setAttributeConfig("disabled", { 25139 25140 value: (oAttributes.disabled || false), 25141 validator: Lang.isBoolean, 25142 method: this._setDisabled 25143 25144 }); 25145 25146 25147 /** 25148 * @attribute value 25149 * @description Object specifying the value for the button group. 25150 * @default null 25151 * @type Object 25152 */ 25153 this.setAttributeConfig("value", { 25154 25155 value: oAttributes.value 25156 25157 }); 25158 25159 25160 /** 25161 * @attribute container 25162 * @description HTML element reference or string specifying the id 25163 * attribute of the HTML element that the button group's markup 25164 * should be rendered into. 25165 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 25166 * level-one-html.html#ID-58190037">HTMLElement</a>|String 25167 * @default null 25168 * @writeonce 25169 */ 25170 this.setAttributeConfig("container", { 25171 25172 value: oAttributes.container, 25173 writeOnce: true 25174 25175 }); 25176 25177 25178 /** 25179 * @attribute checkedButton 25180 * @description Reference for the button in the button group that 25181 * is checked. 25182 * @type {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 25183 * @default null 25184 */ 25185 this.setAttributeConfig("checkedButton", { 25186 25187 value: null 25188 25189 }); 25190 25191 }, 25192 25193 25194 /** 25195 * @method addButton 25196 * @description Adds the button to the button group. 25197 * @param {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 25198 * p_oButton Object reference for the <a href="YAHOO.widget.Button.html"> 25199 * YAHOO.widget.Button</a> instance to be added to the button group. 25200 * @param {String} p_oButton String specifying the id attribute of the 25201 * <code><input></code> or <code><span></code> element 25202 * to be used to create the button to be added to the button group. 25203 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 25204 * level-one-html.html#ID-6043025">HTMLInputElement</a>|<a href=" 25205 * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html# 25206 * ID-33759296">HTMLElement</a>} p_oButton Object reference for the 25207 * <code><input></code> or <code><span></code> element 25208 * to be used to create the button to be added to the button group. 25209 * @param {Object} p_oButton Object literal specifying a set of 25210 * <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> 25211 * configuration attributes used to configure the button to be added to 25212 * the button group. 25213 * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 25214 */ 25215 addButton: function (p_oButton) { 25216 25217 var oButton, 25218 oButtonElement, 25219 oGroupElement, 25220 nIndex, 25221 sButtonName, 25222 sGroupName; 25223 25224 25225 if (p_oButton instanceof Button && 25226 p_oButton.get("type") == "radio") { 25227 25228 oButton = p_oButton; 25229 25230 } 25231 else if (!Lang.isString(p_oButton) && !p_oButton.nodeName) { 25232
25233 p_oButton.type = "radio"; 25234 25235 oButton = new Button(p_oButton); 25236 25237 } 25238 else { 25239 25240 oButton = new Button(p_oButton, { type: "radio" }); 25241 25242 } 25243 25244 25245 if (oButton) { 25246 25247 nIndex = this._buttons.length; 25248 sButtonName = oButton.get("name"); 25249 sGroupName = this.get("name"); 25250 25251 oButton.index = nIndex; 25252 25253 this._buttons[nIndex] = oButton; 25254 m_oButtons[oButton.get("id")] = oButton; 25255 25256 25257 if (sButtonName != sGroupName) { 25258 25259 oButton.set("name", sGroupName); 25260 25261 } 25262 25263 25264 if (this.get("disabled")) { 25265 25266 oButton.set("disabled", true); 25267 25268 } 25269 25270 25271 if (oButton.get("checked")) { 25272 25273 this.set("checkedButton", oButton); 25274 25275 } 25276 25277 25278 oButtonElement = oButton.get("element"); 25279 oGroupElement = this.get("element"); 25280 25281 if (oButtonElement.parentNode != oGroupElement) { 25282 25283 oGroupElement.appendChild(oButtonElement); 25284 25285 } 25286 25287 25288 oButton.on("checkedChange", 25289 this._onButtonCheckedChange, oButton, this); 25290 25291 this.logger.log("Button " + oButton.get("id") + " added."); 25292 25293 } 25294 25295 return oButton; 25296 25297 }, 25298 25299 25300 /** 25301 * @method addButtons 25302 * @description Adds the array of buttons to the button group. 25303 * @param {Array} p_aButtons Array of <a href="YAHOO.widget.Button.html"> 25304 * YAHOO.widget.Button</a> instances to be added 25305 * to the button group. 25306 * @param {Array} p_aButtons Array of strings specifying the id 25307 * attribute of the <code><input></code> or <code><span> 25308 * </code> elements to be used to create the buttons to be added to the 25309 * button group. 25310 * @param {Array} p_aButtons Array of object references for the 25311 * <code><input></code> or <code><span></code> elements 25312 * to be used to create the buttons to be added to the button group. 25313 * @param {Array} p_aButtons Array of object literals, each containing 25314 * a set of <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> 25315 * configuration attributes used to configure each button to be added 25316 * to the button group. 25317 * @return {Array} 25318 */ 25319 addButtons: function (p_aButtons) { 25320 25321 var nButtons, 25322 oButton, 25323 aButtons, 25324 i; 25325 25326 if (Lang.isArray(p_aButtons)) { 25327 25328 nButtons = p_aButtons.length; 25329 aButtons = []; 25330 25331 if (nButtons > 0) { 25332 25333 for (i = 0; i < nButtons; i++) { 25334 25335 oButton = this.addButton(p_aButtons[i]); 25336 25337 if (oButton) { 25338 25339 aButtons[aButtons.length] = oButton; 25340 25341 } 25342 25343 } 25344 25345 } 25346 25347 } 25348 25349 return aButtons; 25350 25351 }, 25352 25353 25354 /** 25355 * @method removeButton 25356 * @description Removes the button at the specified index from the 25357 * button group. 25358 * @param {Number} p_nIndex Number specifying the index of the button 25359 * to be removed from the button group. 25360 */ 25361 removeButton: function (p_nIndex) { 25362 25363 var oButton = this.getButton(p_nIndex), 25364 nButtons, 25365 i; 25366 25367 if (oButton) { 25368 25369 this.logger.log("Removing button " + oButton.get("id") + "."); 25370 25371 this._buttons.splice(p_nIndex, 1); 25372 delete m_oButtons[oButton.get("id")]; 25373 25374 oButton.removeListener("checkedChange", 25375 this._onButtonCheckedChange); 25376 25377 oButton.destroy(); 25378 25379 25380 nButtons = this._buttons.length; 25381 25382 if (nButtons > 0) { 25383 25384 i = this._buttons.length - 1; 25385 25386 do { 25387 25388 this._buttons[i].index = i; 25389 25390 } 25391 while (i--); 25392 25393 } 25394 25395 this.logger.log("Button " + oButton.get("id") + " removed."); 25396 25397 } 25398 25399 }, 25400 25401 25402 /** 25403 * @method getButton
25404 * @description Returns the button at the specified index. 25405 * @param {Number} p_nIndex The index of the button to retrieve from the 25406 * button group. 25407 * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 25408 */ 25409 getButton: function (p_nIndex) { 25410 25411 return this._buttons[p_nIndex]; 25412 25413 }, 25414 25415 25416 /** 25417 * @method getButtons 25418 * @description Returns an array of the buttons in the button group. 25419 * @return {Array} 25420 */ 25421 getButtons: function () { 25422 25423 return this._buttons; 25424 25425 }, 25426 25427 25428 /** 25429 * @method getCount 25430 * @description Returns the number of buttons in the button group. 25431 * @return {Number} 25432 */ 25433 getCount: function () { 25434 25435 return this._buttons.length; 25436 25437 }, 25438 25439 25440 /** 25441 * @method focus 25442 * @description Sets focus to the button at the specified index. 25443 * @param {Number} p_nIndex Number indicating the index of the button 25444 * to focus. 25445 */ 25446 focus: function (p_nIndex) { 25447 25448 var oButton, 25449 nButtons, 25450 i; 25451 25452 if (Lang.isNumber(p_nIndex)) { 25453 25454 oButton = this._buttons[p_nIndex]; 25455 25456 if (oButton) { 25457 25458 oButton.focus(); 25459 25460 } 25461 25462 } 25463 else { 25464 25465 nButtons = this.getCount(); 25466 25467 for (i = 0; i < nButtons; i++) { 25468 25469 oButton = this._buttons[i]; 25470 25471 if (!oButton.get("disabled")) { 25472 25473 oButton.focus(); 25474 break; 25475 25476 } 25477 25478 } 25479 25480 } 25481 25482 }, 25483 25484 25485 /** 25486 * @method check 25487 * @description Checks the button at the specified index. 25488 * @param {Number} p_nIndex Number indicating the index of the button 25489 * to check. 25490 */ 25491 check: function (p_nIndex) { 25492 25493 var oButton = this.getButton(p_nIndex); 25494 25495 if (oButton) { 25496 25497 oButton.set("checked", true); 25498 25499 } 25500 25501 }, 25502 25503 25504 /** 25505 * @method destroy 25506 * @description Removes the button group's element from its parent 25507 * element and removes all event handlers. 25508 */ 25509 destroy: function () { 25510 25511 this.logger.log("Destroying..."); 25512 25513 var nButtons = this._buttons.length, 25514 oElement = this.get("element"), 25515 oParentNode = oElement.parentNode, 25516 i; 25517 25518 if (nButtons > 0) { 25519 25520 i = this._buttons.length - 1; 25521 25522 do { 25523 25524 this._buttons[i].destroy(); 25525 25526 } 25527 while (i--); 25528 25529 } 25530 25531 this.logger.log("Removing DOM event handlers."); 25532 25533 Event.purgeElement(oElement); 25534 25535 this.logger.log("Removing from document."); 25536 25537 oParentNode.removeChild(oElement); 25538 25539 }, 25540 25541 25542 /** 25543 * @method toString 25544 * @description Returns a string representing the button group. 25545 * @return {String} 25546 */ 25547 toString: function () { 25548 25549 return ("ButtonGroup " + this.get("id")); 25550 25551 } 25552 25553 }); 25554 25555})(); 25556YAHOO.register("button", YAHOO.widget.Button, {version: "2.9.0", build: "2800"}); 25557(function () { 25558 25559 var Lang = YAHOO.lang, 25560 UA = YAHOO.env.ua, 25561 25562 ButtonPrototype = YAHOO.widget.Button.prototype, 25563 fnButtonInitAttributes = ButtonPrototype.initAttributes, 25564 25565 ButtonGroupPrototype = YAHOO.widget.ButtonGroup.prototype, 25566 fnButtonGroupInitAttributes = ButtonGroupPrototype.initAttributes, 25567 fnButtonGroupAddButton = ButtonGroupPrototype.addButton, 25568 25569 m_bUseARIA = (UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8), 25570 25571 // Private constants for strings 25572 25573 _ARIA_PREFIX = "aria-", 25574 _USE_ARIA = "usearia", 25575 _CHECKED = "checked", 25576 _TYPE = "type", 25577 _MENU = "menu", 25578 _SPLIT = "split", 25579 _HAS_POPUP = "haspopup", 25580 _RENDER = "render", 25581 _RADIO = "radio",
25582 _CHECKBOX = "checkbox", 25583 _ROLE = "role", 25584 _CHECKED_CHANGE = "checkedChange", 25585 _PRESENTATION = "presentation", 25586 _ELEMENT = "element", 25587 _RADIO_GROUP = "radiogroup", 25588 _CHECKED_BUTTON_CHANGE = "checkedButtonChange", 25589 _APPEND_TO = "appendTo", 25590 _LABELLED_BY = "labelledby", 25591 _DESCRIBED_BY = "describedby", 25592 _ID = "id"; 25593 25594 25595 if (m_bUseARIA) { 25596 25597 ButtonPrototype.RADIO_DEFAULT_TITLE = ""; 25598 ButtonPrototype.RADIO_CHECKED_TITLE = ""; 25599 ButtonPrototype.CHECKBOX_DEFAULT_TITLE = ""; 25600 ButtonPrototype.CHECKBOX_CHECKED_TITLE = ""; 25601 25602 } 25603 25604 25605 // Button ARIA plugin 25606 25607 var setARIARole = function (element, role) { 25608 25609 element.setAttribute(_ROLE, role); 25610 25611 }; 25612 25613 25614 var setARIAProperty = function (element, property, value) { 25615 25616 element.setAttribute((_ARIA_PREFIX + property), value); 25617 25618 }; 25619 25620 25621 var enableARIAForMenu = function (type, args, button) { 25622 25623 this.cfg.setProperty(_USE_ARIA, true); 25624 this.cfg.setProperty(_LABELLED_BY, button.get(_ID)); 25625 25626 }; 25627 25628 25629 var onAppendTo = function () { 25630 25631 this._menu.subscribe(_RENDER, enableARIAForMenu, this); 25632 25633 }; 25634 25635 25636 var toggleARIACheckedState = function (event) { 25637 25638 setARIAProperty(this._button, _CHECKED, event.newValue); 25639 25640 }; 25641 25642 25643 Lang.augmentObject(ButtonPrototype, { 25644 25645 _setUseARIA: function (p_bUseARIA) { 25646 25647 var sType = this.get(_TYPE), 25648 oButtonEl = this._button; 25649 25650 25651 if (p_bUseARIA) { 25652 25653 switch (sType) { 25654 25655 case _MENU: 25656 case _SPLIT: 25657 25658 setARIAProperty(oButtonEl, _HAS_POPUP, true); 25659 25660 this.on(_APPEND_TO, onAppendTo); 25661 25662 break; 25663 25664 25665 case _RADIO: 25666 case _CHECKBOX: 25667 25668 setARIARole(oButtonEl, sType); 25669 25670 setARIAProperty(oButtonEl, _CHECKED, this.get(_CHECKED)); 25671 25672 this.on(_CHECKED_CHANGE, toggleARIACheckedState); 25673 25674 break; 25675 25676 } 25677 25678 } 25679 25680 }, 25681 25682 25683 initAttributes: function (p_oAttributes) { 25684 25685 /** 25686 * @attribute usearia 25687 * @description Boolean indicating if use of the WAI-ARIA Roles and States should 25688 * be enabled. 25689 * @type Boolean 25690 * @default true for Firefox 3 and IE 8, false for all other browsers. 25691 */ 25692 this.setAttributeConfig(_USE_ARIA, { 25693 25694 value: p_oAttributes.usearia || m_bUseARIA, 25695 validator: Lang.isBoolean, 25696 writeOnce: true, 25697 method: this._setUseARIA 25698 25699 }); 25700 25701 fnButtonInitAttributes.apply(this, arguments); 25702 25703 if (m_bUseARIA) { 25704 this.set(_USE_ARIA, true); 25705 } 25706 25707 } 25708 25709 }, "initAttributes", "_setUseARIA"); 25710 25711 25712 25713 // ButtonGroup ARIA plugin 25714 25715 var updateTabIndex = function (event) { 25716 25717 var oPreviousButton = event.prevValue; 25718 25719 if (oPreviousButton) { 25720 oPreviousButton._button.tabIndex = -1; 25721 } 25722 25723 event.newValue._button.tabIndex = 0; 25724 25725 }; 25726 25727 25728 Lang.augmentObject(ButtonGroupPrototype, { 25729 25730 addButton: function (p_oButton) { 25731 25732 var oButton = fnButtonGroupAddButton.call(this, p_oButton), 25733 oButtonEl, 25734 oParentNode; 25735 25736 if (this.get(_USE_ARIA)) { 25737 25738 oButton.set(_USE_ARIA, true); 25739 25740 oButtonEl = oButton._button; 25741 oParentNode = oButtonEl.parentNode; 25742 25743 setARIARole(oParentNode, _PRESENTATION); 25744 setARIARole(oParentNode.parentNode, _PRESENTATION); 25745 25746 oButtonEl.tabIndex = oButton.get(_CHECKED) ? 0 : -1; 25747 25748 } 25749 25750 return oButton; 25751 25752 }, 25753 25754 _setUseARIA: function (p_bUseARIA) { 25755 25756 if (p_bUseARIA) { 25757 25758 setARIARole(this.get(_ELEMENT), _RADIO_GROUP); 25759 25760 this.on(_CHECKED_BUTTON_CHANGE, updateTabIndex); 25761 25762 } 25763 25764 }, 25765 25766 _setLabelledBy: function (id) { 25767 25768 if (this.get(_USE_ARIA)) { 25769 setARIAProperty(this.get(_ELEMENT), _LABELLED_BY, id); 25770 } 25771 25772 }, 25773 25774 _setDescribedBy: function (id) { 25775 25776 if (this.get(_USE_ARIA)) { 25777 setARIAProperty(this.get(_ELEMENT), _DESCRIBED_BY, id); 25778 } 25779 25780 }, 25781 25782 initAttributes: function (p_oAttributes) { 25783 25784 /** 25785 * @attribute usearia 25786 * @description Boolean indicating if use of the WAI-ARIA Roles and States should 25787 * be enabled. 25788 * @type Boolean 25789 * @default true for Firefox 3 and IE 8, false for all other browsers. 25790 */ 25791 this.setAttributeConfig(_USE_ARIA, { 25792 25793 value: p_oAttributes.usearia || m_bUseARIA, 25794 validator: Lang.isBoolean, 25795 writeOnce: true, 25796 method: this._setUseARIA 25797 25798 }); 25799 25800 25801 /** 25802 * @attribute labelledby
25803 * @description String representing the id of the element that labels the ButtonGroup. 25804 * Maps directly to the <a href="http://www.w3.org/TR/wai-aria/#labelledby"> 25805 * <code>aria-labelledby</code></a> attribute. 25806 * @type String 25807 * @default null 25808 */ 25809 this.setAttributeConfig(_LABELLED_BY, { 25810 25811 value: p_oAttributes.labelledby, 25812 validator: Lang.isString, 25813 method: this._setLabelledBy 25814 25815 }); 25816 25817 25818 /** 25819 * @attribute describedby 25820 * @description String representing the id of the element that describes the ButtonGroup. 25821 * Maps directly to the <a href="http://www.w3.org/TR/wai-aria/#describedby"> 25822 * <code>aria-describedby</code></a> attribute. 25823 * @type String 25824 * @default null 25825 */ 25826 this.setAttributeConfig(_DESCRIBED_BY, { 25827 25828 value: p_oAttributes.describedby, 25829 validator: Lang.isString, 25830 method: this._setDescribedBy 25831 25832 }); 25833 25834 25835 fnButtonGroupInitAttributes.apply(this, arguments); 25836 25837 25838 if (m_bUseARIA) { 25839 this.set(_USE_ARIA, true); 25840 } 25841 25842 } 25843 25844 }, "initAttributes", "_setUseARIA", "_setLabelledBy", "_setDescribedBy", "addButton"); 25845 25846 25847}()); 25848YAHOO.register("buttonariaplugin", YAHOO.widget.Button, {version: "@VERSION@", build: "@BUILD@"}); 25849/* 25850Copyright (c) 2011, Yahoo! Inc. All rights reserved. 25851Code licensed under the BSD License: 25852http://developer.yahoo.com/yui/license.html 25853version: 2.9.0 25854*/ 25855(function () { 25856 25857 /** 25858 * Config is a utility used within an Object to allow the implementer to 25859 * maintain a list of local configuration properties and listen for changes 25860 * to those properties dynamically using CustomEvent. The initial values are 25861 * also maintained so that the configuration can be reset at any given point 25862 * to its initial state. 25863 * @namespace YAHOO.util 25864 * @class Config 25865 * @constructor 25866 * @param {Object} owner The owner Object to which this Config Object belongs 25867 */ 25868 YAHOO.util.Config = function (owner) { 25869 25870 if (owner) { 25871 this.init(owner); 25872 } 25873 25874 if (!owner) { YAHOO.log("No owner specified for Config object", "error", "Config"); } 25875 25876 }; 25877 25878 25879 var Lang = YAHOO.lang, 25880 CustomEvent = YAHOO.util.CustomEvent, 25881 Config = YAHOO.util.Config; 25882 25883 25884 /** 25885 * Constant representing the CustomEvent type for the config changed event. 25886 * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT 25887 * @private 25888 * @static 25889 * @final 25890 */ 25891 Config.CONFIG_CHANGED_EVENT = "configChanged"; 25892 25893 /** 25894 * Constant representing the boolean type string 25895 * @property YAHOO.util.Config.BOOLEAN_TYPE 25896 * @private 25897 * @static 25898 * @final 25899 */ 25900 Config.BOOLEAN_TYPE = "boolean"; 25901 25902 Config.prototype = { 25903 25904 /** 25905 * Object reference to the owner of this Config Object 25906 * @property owner 25907 * @type Object 25908 */ 25909 owner: null, 25910 25911 /** 25912 * Boolean flag that specifies whether a queue is currently 25913 * being executed 25914 * @property queueInProgress 25915 * @type Boolean 25916 */ 25917 queueInProgress: false, 25918 25919 /** 25920 * Maintains the local collection of configuration property objects and 25921 * their specified values 25922 * @property config 25923 * @private 25924 * @type Object 25925 */ 25926 config: null, 25927 25928 /** 25929 * Maintains the local collection of configuration property objects as 25930 * they were initially applied. 25931 * This object is used when resetting a property. 25932 * @property initialConfig 25933 * @private 25934 * @type Object 25935 */ 25936 initialConfig: null, 25937 25938 /** 25939 * Maintains the local, normalized CustomEvent queue 25940 * @property eventQueue 25941 * @private 25942 * @type Object 25943 */ 25944 eventQueue: null, 25945 25946 /** 25947 * Custom Event, notifying subscribers when Config properties are set 25948 * (setProperty is called without the silent flag 25949 * @event configChangedEvent 25950 */ 25951 configChangedEvent: null, 25952 25953 /** 25954 * Initializes the configuration Object and all of its local members. 25955 * @method init 25956 * @param {Object} owner The owner Object to which this Config 25957 * Object belongs 25958 */ 25959 init: function (owner) { 25960 25961 this.owner = owner; 25962 25963 this.configChangedEvent =
25964 this.createEvent(Config.CONFIG_CHANGED_EVENT); 25965 25966 this.configChangedEvent.signature = CustomEvent.LIST; 25967 this.queueInProgress = false; 25968 this.config = {}; 25969 this.initialConfig = {}; 25970 this.eventQueue = []; 25971 25972 }, 25973 25974 /** 25975 * Validates that the value passed in is a Boolean. 25976 * @method checkBoolean 25977 * @param {Object} val The value to validate 25978 * @return {Boolean} true, if the value is valid 25979 */ 25980 checkBoolean: function (val) { 25981 return (typeof val == Config.BOOLEAN_TYPE); 25982 }, 25983 25984 /** 25985 * Validates that the value passed in is a number. 25986 * @method checkNumber 25987 * @param {Object} val The value to validate 25988 * @return {Boolean} true, if the value is valid 25989 */ 25990 checkNumber: function (val) { 25991 return (!isNaN(val)); 25992 }, 25993 25994 /** 25995 * Fires a configuration property event using the specified value. 25996 * @method fireEvent 25997 * @private 25998 * @param {String} key The configuration property's name 25999 * @param {value} Object The value of the correct type for the property 26000 */ 26001 fireEvent: function ( key, value ) { 26002 YAHOO.log("Firing Config event: " + key + "=" + value, "info", "Config"); 26003 var property = this.config[key]; 26004 26005 if (property && property.event) { 26006 property.event.fire(value); 26007 } 26008 }, 26009 26010 /** 26011 * Adds a property to the Config Object's private config hash. 26012 * @method addProperty 26013 * @param {String} key The configuration property's name 26014 * @param {Object} propertyObject The Object containing all of this 26015 * property's arguments 26016 */ 26017 addProperty: function ( key, propertyObject ) { 26018 key = key.toLowerCase(); 26019 YAHOO.log("Added property: " + key, "info", "Config"); 26020 26021 this.config[key] = propertyObject; 26022 26023 propertyObject.event = this.createEvent(key, { scope: this.owner }); 26024 propertyObject.event.signature = CustomEvent.LIST; 26025 26026 26027 propertyObject.key = key; 26028 26029 if (propertyObject.handler) { 26030 propertyObject.event.subscribe(propertyObject.handler, 26031 this.owner); 26032 } 26033 26034 this.setProperty(key, propertyObject.value, true); 26035 26036 if (! propertyObject.suppressEvent) { 26037 this.queueProperty(key, propertyObject.value); 26038 } 26039 26040 }, 26041 26042 /** 26043 * Returns a key-value configuration map of the values currently set in 26044 * the Config Object. 26045 * @method getConfig 26046 * @return {Object} The current config, represented in a key-value map 26047 */ 26048 getConfig: function () { 26049 26050 var cfg = {}, 26051 currCfg = this.config, 26052 prop, 26053 property; 26054 26055 for (prop in currCfg) { 26056 if (Lang.hasOwnProperty(currCfg, prop)) { 26057 property = currCfg[prop]; 26058 if (property && property.event) { 26059 cfg[prop] = property.value; 26060 } 26061 } 26062 } 26063 26064 return cfg; 26065 }, 26066 26067 /** 26068 * Returns the value of specified property. 26069 * @method getProperty 26070 * @param {String} key The name of the property 26071 * @return {Object} The value of the specified property 26072 */ 26073 getProperty: function (key) { 26074 var property = this.config[key.toLowerCase()]; 26075 if (property && property.event) { 26076 return property.value; 26077 } else { 26078 return undefined; 26079 } 26080 }, 26081 26082 /** 26083 * Resets the specified property's value to its initial value. 26084 * @method resetProperty 26085 * @param {String} key The name of the property 26086 * @return {Boolean} True is the property was reset, false if not 26087 */ 26088 resetProperty: function (key) { 26089 key = key.toLowerCase(); 26090 26091 var property = this.config[key]; 26092 26093 if (property && property.event) { 26094 if (key in this.initialConfig) { 26095 this.setProperty(key, this.initialConfig[key]); 26096 return true; 26097 } 26098 } else { 26099 return false;
26100 } 26101 }, 26102 26103 /** 26104 * Sets the value of a property. If the silent property is passed as 26105 * true, the property's event will not be fired. 26106 * @method setProperty 26107 * @param {String} key The name of the property 26108 * @param {String} value The value to set the property to 26109 * @param {Boolean} silent Whether the value should be set silently, 26110 * without firing the property event. 26111 * @return {Boolean} True, if the set was successful, false if it failed. 26112 */ 26113 setProperty: function (key, value, silent) { 26114 26115 var property; 26116 26117 key = key.toLowerCase(); 26118 YAHOO.log("setProperty: " + key + "=" + value, "info", "Config"); 26119 26120 if (this.queueInProgress && ! silent) { 26121 // Currently running through a queue... 26122 this.queueProperty(key,value); 26123 return true; 26124 26125 } else { 26126 property = this.config[key]; 26127 if (property && property.event) { 26128 if (property.validator && !property.validator(value)) { 26129 return false; 26130 } else { 26131 property.value = value; 26132 if (! silent) { 26133 this.fireEvent(key, value); 26134 this.configChangedEvent.fire([key, value]); 26135 } 26136 return true; 26137 } 26138 } else { 26139 return false; 26140 } 26141 } 26142 }, 26143 26144 /** 26145 * Sets the value of a property and queues its event to execute. If the 26146 * event is already scheduled to execute, it is 26147 * moved from its current position to the end of the queue. 26148 * @method queueProperty 26149 * @param {String} key The name of the property 26150 * @param {String} value The value to set the property to 26151 * @return {Boolean} true, if the set was successful, false if 26152 * it failed. 26153 */ 26154 queueProperty: function (key, value) { 26155 26156 key = key.toLowerCase(); 26157 YAHOO.log("queueProperty: " + key + "=" + value, "info", "Config"); 26158 26159 var property = this.config[key], 26160 foundDuplicate = false, 26161 iLen, 26162 queueItem, 26163 queueItemKey, 26164 queueItemValue, 26165 sLen, 26166 supercedesCheck, 26167 qLen, 26168 queueItemCheck, 26169 queueItemCheckKey, 26170 queueItemCheckValue, 26171 i, 26172 s, 26173 q; 26174 26175 if (property && property.event) { 26176 26177 if (!Lang.isUndefined(value) && property.validator && 26178 !property.validator(value)) { // validator 26179 return false; 26180 } else { 26181 26182 if (!Lang.isUndefined(value)) { 26183 property.value = value; 26184 } else { 26185 value = property.value; 26186 } 26187 26188 foundDuplicate = false; 26189 iLen = this.eventQueue.length; 26190 26191 for (i = 0; i < iLen; i++) { 26192 queueItem = this.eventQueue[i]; 26193 26194 if (queueItem) { 26195 queueItemKey = queueItem[0]; 26196 queueItemValue = queueItem[1]; 26197 26198 if (queueItemKey == key) { 26199 26200 /* 26201 found a dupe... push to end of queue, null 26202 current item, and break 26203 */ 26204 26205 this.eventQueue[i] = null; 26206 26207 this.eventQueue.push( 26208 [key, (!Lang.isUndefined(value) ? 26209 value : queueItemValue)]); 26210 26211 foundDuplicate = true; 26212 break; 26213 } 26214 } 26215 } 26216 26217 // this is a refire, or a new property in the queue 26218 26219 if (! foundDuplicate && !Lang.isUndefined(value)) { 26220 this.eventQueue.push([key, value]); 26221 } 26222 } 26223 26224 if (property.supercedes) { 26225 26226 sLen = property.supercedes.length; 26227 26228 for (s = 0; s < sLen; s++) { 26229 26230 supercedesCheck = property.supercedes[s]; 26231 qLen = this.eventQueue.length; 26232 26233 for (q = 0; q < qLen; q++) { 26234 queueItemCheck = this.eventQueue[q]; 26235 26236 if (queueItemCheck) { 26237 queueItemCheckKey = queueItemCheck[0]; 26238 queueItemCheckValue = queueItemCheck[1]; 26239 26240 if (queueItemCheckKey == 26241 supercedesCheck.toLowerCase() ) { 26242 26243 this.eventQueue.push([queueItemCheckKey, 26244 queueItemCheckValue]); 26245 26246 this.eventQueue[q] = null; 26247 break; 26248 26249 } 26250 } 26251 } 26252 } 26253 } 26254 26255 YAHOO.log("Config event queue: " + this.outputEventQueue(), "info", "Config"); 26256 26257 return true; 26258 } else { 26259 return false;
26260 } 26261 }, 26262 26263 /** 26264 * Fires the event for a property using the property's current value. 26265 * @method refireEvent 26266 * @param {String} key The name of the property 26267 */ 26268 refireEvent: function (key) { 26269 26270 key = key.toLowerCase(); 26271 26272 var property = this.config[key]; 26273 26274 if (property && property.event && 26275 26276 !Lang.isUndefined(property.value)) { 26277 26278 if (this.queueInProgress) { 26279 26280 this.queueProperty(key); 26281 26282 } else { 26283 26284 this.fireEvent(key, property.value); 26285 26286 } 26287 26288 } 26289 }, 26290 26291 /** 26292 * Applies a key-value Object literal to the configuration, replacing 26293 * any existing values, and queueing the property events. 26294 * Although the values will be set, fireQueue() must be called for their 26295 * associated events to execute. 26296 * @method applyConfig 26297 * @param {Object} userConfig The configuration Object literal 26298 * @param {Boolean} init When set to true, the initialConfig will 26299 * be set to the userConfig passed in, so that calling a reset will 26300 * reset the properties to the passed values. 26301 */ 26302 applyConfig: function (userConfig, init) { 26303 26304 var sKey, 26305 oConfig; 26306 26307 if (init) { 26308 oConfig = {}; 26309 for (sKey in userConfig) { 26310 if (Lang.hasOwnProperty(userConfig, sKey)) { 26311 oConfig[sKey.toLowerCase()] = userConfig[sKey]; 26312 } 26313 } 26314 this.initialConfig = oConfig; 26315 } 26316 26317 for (sKey in userConfig) { 26318 if (Lang.hasOwnProperty(userConfig, sKey)) { 26319 this.queueProperty(sKey, userConfig[sKey]); 26320 } 26321 } 26322 }, 26323 26324 /**
26325 * Refires the events for all configuration properties using their 26326 * current values. 26327 * @method refresh 26328 */ 26329 refresh: function () { 26330 26331 var prop; 26332 26333 for (prop in this.config) { 26334 if (Lang.hasOwnProperty(this.config, prop)) { 26335 this.refireEvent(prop); 26336 } 26337 } 26338 }, 26339 26340 /** 26341 * Fires the normalized list of queued property change events 26342 * @method fireQueue 26343 */ 26344 fireQueue: function () { 26345 26346 var i, 26347 queueItem, 26348 key, 26349 value, 26350 property; 26351 26352 this.queueInProgress = true; 26353 for (i = 0;i < this.eventQueue.length; i++) { 26354 queueItem = this.eventQueue[i]; 26355 if (queueItem) { 26356 26357 key = queueItem[0]; 26358 value = queueItem[1]; 26359 property = this.config[key]; 26360 26361 property.value = value; 26362 26363 // Clear out queue entry, to avoid it being 26364 // re-added to the queue by any queueProperty/supercedes 26365 // calls which are invoked during fireEvent 26366 this.eventQueue[i] = null; 26367 26368 this.fireEvent(key,value); 26369 } 26370 } 26371 26372 this.queueInProgress = false; 26373 this.eventQueue = []; 26374 }, 26375 26376 /** 26377 * Subscribes an external handler to the change event for any 26378 * given property. 26379 * @method subscribeToConfigEvent 26380 * @param {String} key The property name 26381 * @param {Function} handler The handler function to use subscribe to 26382 * the property's event 26383 * @param {Object} obj The Object to use for scoping the event handler 26384 * (see CustomEvent documentation) 26385 * @param {Boolean} overrideContext Optional. If true, will override 26386 * "this" within the handler to map to the scope Object passed into the 26387 * method. 26388 * @return {Boolean} True, if the subscription was successful, 26389 * otherwise false. 26390 */ 26391 subscribeToConfigEvent: function (key, handler, obj, overrideContext) { 26392 26393 var property = this.config[key.toLowerCase()]; 26394 26395 if (property && property.event) { 26396 if (!Config.alreadySubscribed(property.event, handler, obj)) { 26397 property.event.subscribe(handler, obj, overrideContext); 26398 } 26399 return true; 26400 } else { 26401 return false; 26402 } 26403 26404 }, 26405 26406 /** 26407 * Unsubscribes an external handler from the change event for any 26408 * given property. 26409 * @method unsubscribeFromConfigEvent 26410 * @param {String} key The property name 26411 * @param {Function} handler The handler function to use subscribe to 26412 * the property's event 26413 * @param {Object} obj The Object to use for scoping the event 26414 * handler (see CustomEvent documentation) 26415 * @return {Boolean} True, if the unsubscription was successful, 26416 * otherwise false. 26417 */ 26418 unsubscribeFromConfigEvent: function (key, handler, obj) { 26419 var property = this.config[key.toLowerCase()]; 26420 if (property && property.event) { 26421 return property.event.unsubscribe(handler, obj); 26422 } else { 26423 return false; 26424 } 26425 }, 26426 26427 /** 26428 * Returns a string representation of the Config object 26429 * @method toString 26430 * @return {String} The Config object in string format. 26431 */ 26432 toString: function () { 26433 var output = "Config"; 26434 if (this.owner) { 26435 output += " [" + this.owner.toString() + "]"; 26436 } 26437 return output; 26438 }, 26439 26440 /** 26441 * Returns a string representation of the Config object's current 26442 * CustomEvent queue 26443 * @method outputEventQueue 26444 * @return {String} The string list of CustomEvents currently queued 26445 * for execution 26446 */ 26447 outputEventQueue: function () { 26448 26449 var output = "", 26450 queueItem, 26451 q, 26452 nQueue = this.eventQueue.length; 26453 26454 for (q = 0; q < nQueue; q++) { 26455 queueItem = this.eventQueue[q]; 26456 if (queueItem) { 26457 output += queueItem[0] + "=" + queueItem[1] + ", "; 26458 } 26459 } 26460 return output; 26461 }, 26462 26463 /** 26464 * Sets all properties to null, unsubscribes all listeners from each 26465 * property's change event and all listeners from the configChangedEvent. 26466 * @method destroy 26467 */ 26468 destroy: function () { 26469 26470 var oConfig = this.config, 26471 sProperty, 26472 oProperty; 26473 26474 26475 for (sProperty in oConfig) { 26476 26477 if (Lang.hasOwnProperty(oConfig, sProperty)) { 26478 26479 oProperty = oConfig[sProperty]; 26480 26481 oProperty.event.unsubscribeAll(); 26482 oProperty.event = null; 26483 26484 } 26485 26486 } 26487 26488 this.configChangedEvent.unsubscribeAll(); 26489 26490 this.configChangedEvent = null; 26491 this.owner = null; 26492 this.config = null; 26493 this.initialConfig = null; 26494 this.eventQueue = null; 26495 26496 } 26497 26498 }; 26499 26500 26501 26502 /** 26503 * Checks to determine if a particular function/Object pair are already 26504 * subscribed to the specified CustomEvent 26505 * @method YAHOO.util.Config.alreadySubscribed 26506 * @static 26507 * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 26508 * the subscriptions 26509 * @param {Function} fn The function to look for in the subscribers list 26510 * @param {Object} obj The execution scope Object for the subscription 26511 * @return {Boolean} true, if the function/Object pair is already subscribed 26512 * to the CustomEvent passed in 26513 */ 26514 Config.alreadySubscribed = function (evt, fn, obj) { 26515 26516 var nSubscribers = evt.subscribers.length, 26517 subsc, 26518 i; 26519 26520 if (nSubscribers > 0) { 26521 i = nSubscribers - 1; 26522 do { 26523 subsc = evt.subscribers[i]; 26524 if (subsc && subsc.obj == obj && subsc.fn == fn) { 26525 return true; 26526 } 26527 } 26528 while (i--); 26529 } 26530 26531 return false;
26532 26533 }; 26534 26535 YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider); 26536 26537}()); 26538(function () { 26539 26540 /** 26541 * The Container family of components is designed to enable developers to 26542 * create different kinds of content-containing modules on the web. Module 26543 * and Overlay are the most basic containers, and they can be used directly 26544 * or extended to build custom containers. Also part of the Container family 26545 * are four UI controls that extend Module and Overlay: Tooltip, Panel, 26546 * Dialog, and SimpleDialog. 26547 * @module container 26548 * @title Container 26549 * @requires yahoo, dom, event 26550 * @optional dragdrop, animation, button 26551 */ 26552 26553 /** 26554 * Module is a JavaScript representation of the Standard Module Format. 26555 * Standard Module Format is a simple standard for markup containers where 26556 * child nodes representing the header, body, and footer of the content are 26557 * denoted using the CSS classes "hd", "bd", and "ft" respectively. 26558 * Module is the base class for all other classes in the YUI 26559 * Container package. 26560 * @namespace YAHOO.widget 26561 * @class Module 26562 * @constructor 26563 * @param {String} el The element ID representing the Module <em>OR</em> 26564 * @param {HTMLElement} el The element representing the Module 26565 * @param {Object} userConfig The configuration Object literal containing 26566 * the configuration that should be set for this module. See configuration 26567 * documentation for more details. 26568 */ 26569 YAHOO.widget.Module = function (el, userConfig) { 26570 if (el) { 26571 this.init(el, userConfig); 26572 } else { 26573 YAHOO.log("No element or element ID specified" + 26574 " for Module instantiation", "error"); 26575 } 26576 }; 26577 26578 var Dom = YAHOO.util.Dom, 26579 Config = YAHOO.util.Config, 26580 Event = YAHOO.util.Event, 26581 CustomEvent = YAHOO.util.CustomEvent, 26582 Module = YAHOO.widget.Module, 26583 UA = YAHOO.env.ua, 26584 26585 m_oModuleTemplate, 26586 m_oHeaderTemplate, 26587 m_oBodyTemplate, 26588 m_oFooterTemplate, 26589 26590 /** 26591 * Constant representing the name of the Module's events 26592 * @property EVENT_TYPES 26593 * @private 26594 * @final 26595 * @type Object 26596 */ 26597 EVENT_TYPES = { 26598 "BEFORE_INIT": "beforeInit", 26599 "INIT": "init", 26600 "APPEND": "append", 26601 "BEFORE_RENDER": "beforeRender", 26602 "RENDER": "render", 26603 "CHANGE_HEADER": "changeHeader", 26604 "CHANGE_BODY": "changeBody", 26605 "CHANGE_FOOTER": "changeFooter", 26606 "CHANGE_CONTENT": "changeContent", 26607 "DESTROY": "destroy", 26608 "BEFORE_SHOW": "beforeShow", 26609 "SHOW": "show", 26610 "BEFORE_HIDE": "beforeHide", 26611 "HIDE": "hide" 26612 }, 26613 26614 /** 26615 * Constant representing the Module's configuration properties 26616 * @property DEFAULT_CONFIG 26617 * @private 26618 * @final 26619 * @type Object 26620 */ 26621 DEFAULT_CONFIG = { 26622 26623 "VISIBLE": { 26624 key: "visible", 26625 value: true, 26626 validator: YAHOO.lang.isBoolean 26627 }, 26628 26629 "EFFECT": { 26630 key: "effect", 26631 suppressEvent: true, 26632 supercedes: ["visible"] 26633 }, 26634 26635 "MONITOR_RESIZE": { 26636 key: "monitorresize", 26637 value: true 26638 }, 26639 26640 "APPEND_TO_DOCUMENT_BODY": { 26641 key: "appendtodocumentbody", 26642 value: false 26643 } 26644 }; 26645 26646 /** 26647 * Constant representing the prefix path to use for non-secure images 26648 * @property YAHOO.widget.Module.IMG_ROOT 26649 * @static 26650 * @final
26651 * @type String 26652 */ 26653 Module.IMG_ROOT = null; 26654 26655 /** 26656 * Constant representing the prefix path to use for securely served images 26657 * @property YAHOO.widget.Module.IMG_ROOT_SSL 26658 * @static 26659 * @final 26660 * @type String 26661 */ 26662 Module.IMG_ROOT_SSL = null; 26663 26664 /** 26665 * Constant for the default CSS class name that represents a Module 26666 * @property YAHOO.widget.Module.CSS_MODULE 26667 * @static 26668 * @final 26669 * @type String 26670 */ 26671 Module.CSS_MODULE = "yui-module"; 26672 26673 /** 26674 * CSS classname representing the module header. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 26675 * @property YAHOO.widget.Module.CSS_HEADER 26676 * @static 26677 * @final 26678 * @type String 26679 */ 26680 Module.CSS_HEADER = "hd"; 26681 26682 /** 26683 * CSS classname representing the module body. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 26684 * @property YAHOO.widget.Module.CSS_BODY 26685 * @static 26686 * @final
26687 * @type String 26688 */ 26689 Module.CSS_BODY = "bd"; 26690 26691 /** 26692 * CSS classname representing the module footer. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 26693 * @property YAHOO.widget.Module.CSS_FOOTER 26694 * @static 26695 * @final 26696 * @type String 26697 */ 26698 Module.CSS_FOOTER = "ft"; 26699 26700 /** 26701 * Constant representing the url for the "src" attribute of the iframe 26702 * used to monitor changes to the browser's base font size 26703 * @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL 26704 * @static 26705 * @final 26706 * @type String 26707 */ 26708 Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;"; 26709 26710 /** 26711 * Constant representing the buffer amount (in pixels) to use when positioning 26712 * the text resize monitor offscreen. The resize monitor is positioned 26713 * offscreen by an amount eqaul to its offsetHeight + the buffer value. 26714 * 26715 * @property YAHOO.widget.Module.RESIZE_MONITOR_BUFFER 26716 * @static 26717 * @type Number 26718 */ 26719 // Set to 1, to work around pixel offset in IE8, which increases when zoom is used 26720 Module.RESIZE_MONITOR_BUFFER = 1; 26721 26722 /** 26723 * Singleton CustomEvent fired when the font size is changed in the browser. 26724 * Opera's "zoom" functionality currently does not support text 26725 * size detection. 26726 * @event YAHOO.widget.Module.textResizeEvent 26727 */ 26728 Module.textResizeEvent = new CustomEvent("textResize"); 26729 26730 /** 26731 * Helper utility method, which forces a document level 26732 * redraw for Opera, which can help remove repaint 26733 * irregularities after applying DOM changes. 26734 * 26735 * @method YAHOO.widget.Module.forceDocumentRedraw 26736 * @static 26737 */ 26738 Module.forceDocumentRedraw = function() { 26739 var docEl = document.documentElement; 26740 if (docEl) { 26741 docEl.className += " "; 26742 docEl.className = YAHOO.lang.trim(docEl.className); 26743 } 26744 }; 26745 26746 function createModuleTemplate() { 26747 26748 if (!m_oModuleTemplate) { 26749 m_oModuleTemplate = document.createElement("div"); 26750 26751 m_oModuleTemplate.innerHTML = ("<div class=\"" + 26752 Module.CSS_HEADER + "\"></div>" + "<div class=\"" + 26753 Module.CSS_BODY + "\"></div><div class=\"" + 26754 Module.CSS_FOOTER + "\"></div>"); 26755 26756 m_oHeaderTemplate = m_oModuleTemplate.firstChild; 26757 m_oBodyTemplate = m_oHeaderTemplate.nextSibling; 26758 m_oFooterTemplate = m_oBodyTemplate.nextSibling; 26759 } 26760 26761 return m_oModuleTemplate; 26762 } 26763 26764 function createHeader() { 26765 if (!m_oHeaderTemplate) { 26766 createModuleTemplate(); 26767 } 26768 return (m_oHeaderTemplate.cloneNode(false)); 26769 } 26770 26771 function createBody() { 26772 if (!m_oBodyTemplate) { 26773 createModuleTemplate(); 26774 } 26775 return (m_oBodyTemplate.cloneNode(false)); 26776 } 26777 26778 function createFooter() { 26779 if (!m_oFooterTemplate) { 26780 createModuleTemplate(); 26781 } 26782 return (m_oFooterTemplate.cloneNode(false)); 26783 } 26784 26785 Module.prototype = { 26786 26787 /** 26788 * The class's constructor function 26789 * @property contructor 26790 * @type Function 26791 */ 26792 constructor: Module, 26793 26794 /** 26795 * The main module element that contains the header, body, and footer 26796 * @property element 26797 * @type HTMLElement 26798 */ 26799 element: null, 26800 26801 /** 26802 * The header element, denoted with CSS class "hd" 26803 * @property header 26804 * @type HTMLElement 26805 */ 26806 header: null, 26807 26808 /** 26809 * The body element, denoted with CSS class "bd" 26810 * @property body 26811 * @type HTMLElement 26812 */ 26813 body: null, 26814 26815 /** 26816 * The footer element, denoted with CSS class "ft" 26817 * @property footer 26818 * @type HTMLElement 26819 */ 26820 footer: null, 26821 26822 /** 26823 * The id of the element 26824 * @property id
26825 * @type String 26826 */ 26827 id: null, 26828 26829 /** 26830 * A string representing the root path for all images created by 26831 * a Module instance. 26832 * @deprecated It is recommend that any images for a Module be applied 26833 * via CSS using the "background-image" property. 26834 * @property imageRoot 26835 * @type String 26836 */ 26837 imageRoot: Module.IMG_ROOT, 26838 26839 /** 26840 * Initializes the custom events for Module which are fired 26841 * automatically at appropriate times by the Module class. 26842 * @method initEvents 26843 */ 26844 initEvents: function () { 26845 26846 var SIGNATURE = CustomEvent.LIST; 26847 26848 /** 26849 * CustomEvent fired prior to class initalization. 26850 * @event beforeInitEvent 26851 * @param {class} classRef class reference of the initializing 26852 * class, such as this.beforeInitEvent.fire(Module) 26853 */ 26854 this.beforeInitEvent = this.createEvent(EVENT_TYPES.BEFORE_INIT); 26855 this.beforeInitEvent.signature = SIGNATURE; 26856 26857 /** 26858 * CustomEvent fired after class initalization. 26859 * @event initEvent 26860 * @param {class} classRef class reference of the initializing 26861 * class, such as this.beforeInitEvent.fire(Module) 26862 */ 26863 this.initEvent = this.createEvent(EVENT_TYPES.INIT); 26864 this.initEvent.signature = SIGNATURE; 26865 26866 /** 26867 * CustomEvent fired when the Module is appended to the DOM 26868 * @event appendEvent 26869 */ 26870 this.appendEvent = this.createEvent(EVENT_TYPES.APPEND); 26871 this.appendEvent.signature = SIGNATURE; 26872 26873 /** 26874 * CustomEvent fired before the Module is rendered 26875 * @event beforeRenderEvent 26876 */ 26877 this.beforeRenderEvent = this.createEvent(EVENT_TYPES.BEFORE_RENDER); 26878 this.beforeRenderEvent.signature = SIGNATURE; 26879 26880 /** 26881 * CustomEvent fired after the Module is rendered 26882 * @event renderEvent 26883 */ 26884 this.renderEvent = this.createEvent(EVENT_TYPES.RENDER); 26885 this.renderEvent.signature = SIGNATURE; 26886 26887 /** 26888 * CustomEvent fired when the header content of the Module 26889 * is modified 26890 * @event changeHeaderEvent 26891 * @param {String/HTMLElement} content String/element representing 26892 * the new header content 26893 */ 26894 this.changeHeaderEvent = this.createEvent(EVENT_TYPES.CHANGE_HEADER); 26895 this.changeHeaderEvent.signature = SIGNATURE; 26896 26897 /** 26898 * CustomEvent fired when the body content of the Module is modified 26899 * @event changeBodyEvent 26900 * @param {String/HTMLElement} content String/element representing 26901 * the new body content 26902 */ 26903 this.changeBodyEvent = this.createEvent(EVENT_TYPES.CHANGE_BODY); 26904 this.changeBodyEvent.signature = SIGNATURE; 26905 26906 /** 26907 * CustomEvent fired when the footer content of the Module 26908 * is modified 26909 * @event changeFooterEvent 26910 * @param {String/HTMLElement} content String/element representing 26911 * the new footer content 26912 */ 26913 this.changeFooterEvent = this.createEvent(EVENT_TYPES.CHANGE_FOOTER); 26914 this.changeFooterEvent.signature = SIGNATURE; 26915 26916 /** 26917 * CustomEvent fired when the content of the Module is modified 26918 * @event changeContentEvent 26919 */ 26920 this.changeContentEvent = this.createEvent(EVENT_TYPES.CHANGE_CONTENT); 26921 this.changeContentEvent.signature = SIGNATURE; 26922 26923 /** 26924 * CustomEvent fired when the Module is destroyed 26925 * @event destroyEvent 26926 */ 26927 this.destroyEvent = this.createEvent(EVENT_TYPES.DESTROY); 26928 this.destroyEvent.signature = SIGNATURE; 26929 26930 /** 26931 * CustomEvent fired before the Module is shown 26932 * @event beforeShowEvent 26933 */ 26934 this.beforeShowEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW); 26935 this.beforeShowEvent.signature = SIGNATURE; 26936 26937 /** 26938 * CustomEvent fired after the Module is shown 26939 * @event showEvent 26940 */ 26941 this.showEvent = this.createEvent(EVENT_TYPES.SHOW); 26942 this.showEvent.signature = SIGNATURE; 26943 26944 /** 26945 * CustomEvent fired before the Module is hidden 26946 * @event beforeHideEvent 26947 */ 26948 this.beforeHideEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE); 26949 this.beforeHideEvent.signature = SIGNATURE; 26950 26951 /** 26952 * CustomEvent fired after the Module is hidden 26953 * @event hideEvent 26954 */
26955 this.hideEvent = this.createEvent(EVENT_TYPES.HIDE); 26956 this.hideEvent.signature = SIGNATURE; 26957 }, 26958 26959 /** 26960 * String identifying whether the current platform is windows or mac. This property 26961 * currently only identifies these 2 platforms, and returns false otherwise. 26962 * @property platform 26963 * @deprecated Use YAHOO.env.ua 26964 * @type {String|Boolean} 26965 */ 26966 platform: function () { 26967 var ua = navigator.userAgent.toLowerCase(); 26968 26969 if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) { 26970 return "windows"; 26971 } else if (ua.indexOf("macintosh") != -1) { 26972 return "mac"; 26973 } else { 26974 return false; 26975 } 26976 }(), 26977 26978 /** 26979 * String representing the user-agent of the browser 26980 * @deprecated Use YAHOO.env.ua 26981 * @property browser 26982 * @type {String|Boolean} 26983 */ 26984 browser: function () { 26985 var ua = navigator.userAgent.toLowerCase(); 26986 /* 26987 Check Opera first in case of spoof and check Safari before 26988 Gecko since Safari's user agent string includes "like Gecko" 26989 */ 26990 if (ua.indexOf('opera') != -1) { 26991 return 'opera'; 26992 } else if (ua.indexOf('msie 7') != -1) { 26993 return 'ie7'; 26994 } else if (ua.indexOf('msie') != -1) { 26995 return 'ie'; 26996 } else if (ua.indexOf('safari') != -1) { 26997 return 'safari'; 26998 } else if (ua.indexOf('gecko') != -1) { 26999 return 'gecko'; 27000 } else { 27001 return false; 27002 } 27003 }(), 27004 27005 /** 27006 * Boolean representing whether or not the current browsing context is 27007 * secure (https) 27008 * @property isSecure 27009 * @type Boolean 27010 */ 27011 isSecure: function () { 27012 if (window.location.href.toLowerCase().indexOf("https") === 0) { 27013 return true; 27014 } else { 27015 return false; 27016 } 27017 }(), 27018 27019 /** 27020 * Initializes the custom events for Module which are fired 27021 * automatically at appropriate times by the Module class. 27022 */ 27023 initDefaultConfig: function () { 27024 // Add properties // 27025 /** 27026 * Specifies whether the Module is visible on the page. 27027 * @config visible 27028 * @type Boolean 27029 * @default true 27030 */ 27031 this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key, { 27032 handler: this.configVisible, 27033 value: DEFAULT_CONFIG.VISIBLE.value, 27034 validator: DEFAULT_CONFIG.VISIBLE.validator 27035 }); 27036 27037 /** 27038 * <p> 27039 * Object or array of objects representing the ContainerEffect 27040 * classes that are active for animating the container. 27041 * </p> 27042 * <p> 27043 * <strong>NOTE:</strong> Although this configuration 27044 * property is introduced at the Module level, an out of the box 27045 * implementation is not shipped for the Module class so setting 27046 * the proroperty on the Module class has no effect. The Overlay 27047 * class is the first class to provide out of the box ContainerEffect 27048 * support. 27049 * </p> 27050 * @config effect 27051 * @type Object 27052 * @default null 27053 */ 27054 this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key, { 27055 handler: this.configEffect, 27056 suppressEvent: DEFAULT_CONFIG.EFFECT.suppressEvent, 27057 supercedes: DEFAULT_CONFIG.EFFECT.supercedes 27058 }); 27059 27060 /** 27061 * Specifies whether to create a special proxy iframe to monitor 27062 * for user font resizing in the document 27063 * @config monitorresize 27064 * @type Boolean 27065 * @default true 27066 */ 27067 this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key, { 27068 handler: this.configMonitorResize, 27069 value: DEFAULT_CONFIG.MONITOR_RESIZE.value 27070 }); 27071 27072 /** 27073 * Specifies if the module should be rendered as the first child 27074 * of document.body or appended as the last child when render is called 27075 * with document.body as the "appendToNode". 27076 * <p> 27077 * Appending to the body while the DOM is still being constructed can 27078 * lead to Operation Aborted errors in IE hence this flag is set to 27079 * false by default. 27080 * </p> 27081 * 27082 * @config appendtodocumentbody 27083 * @type Boolean 27084 * @default false 27085 */ 27086 this.cfg.addProperty(DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.key, { 27087 value: DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.value 27088 }); 27089 }, 27090 27091 /** 27092 * The Module class's initialization method, which is executed for 27093 * Module and all of its subclasses. This method is automatically 27094 * called by the constructor, and sets up all DOM references for 27095 * pre-existing markup, and creates required markup if it is not 27096 * already present. 27097 * <p> 27098 * If the element passed in does not have an id, one will be generated 27099 * for it. 27100 * </p> 27101 * @method init 27102 * @param {String} el The element ID representing the Module <em>OR</em> 27103 * @param {HTMLElement} el The element representing the Module 27104 * @param {Object} userConfig The configuration Object literal 27105 * containing the configuration that should be set for this module. 27106 * See configuration documentation for more details. 27107 */ 27108 init: function (el, userConfig) { 27109 27110 var elId, child; 27111 27112 this.initEvents(); 27113 this.beforeInitEvent.fire(Module); 27114 27115 /** 27116 * The Module's Config object used for monitoring 27117 * configuration properties. 27118 * @property cfg 27119 * @type YAHOO.util.Config 27120 */ 27121 this.cfg = new Config(this); 27122 27123 if (this.isSecure) { 27124 this.imageRoot = Module.IMG_ROOT_SSL; 27125 } 27126 27127 if (typeof el == "string") { 27128 elId = el; 27129 el = document.getElementById(el); 27130 if (! el) { 27131 el = (createModuleTemplate()).cloneNode(false); 27132 el.id = elId; 27133 } 27134 } 27135 27136 this.id = Dom.generateId(el); 27137 this.element = el; 27138 27139 child = this.element.firstChild; 27140 27141 if (child) { 27142 var fndHd = false, fndBd = false, fndFt = false;
27143 do { 27144 // We're looking for elements 27145 if (1 == child.nodeType) { 27146 if (!fndHd && Dom.hasClass(child, Module.CSS_HEADER)) { 27147 this.header = child; 27148 fndHd = true; 27149 } else if (!fndBd && Dom.hasClass(child, Module.CSS_BODY)) { 27150 this.body = child; 27151 fndBd = true; 27152 } else if (!fndFt && Dom.hasClass(child, Module.CSS_FOOTER)){ 27153 this.footer = child; 27154 fndFt = true; 27155 } 27156 } 27157 } while ((child = child.nextSibling)); 27158 } 27159 27160 this.initDefaultConfig(); 27161 27162 Dom.addClass(this.element, Module.CSS_MODULE); 27163 27164 if (userConfig) { 27165 this.cfg.applyConfig(userConfig, true); 27166 } 27167 27168 /* 27169 Subscribe to the fireQueue() method of Config so that any 27170 queued configuration changes are excecuted upon render of 27171 the Module 27172 */ 27173 27174 if (!Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) { 27175 this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true); 27176 } 27177 27178 this.initEvent.fire(Module); 27179 }, 27180 27181 /** 27182 * Initialize an empty IFRAME that is placed out of the visible area 27183 * that can be used to detect text resize. 27184 * @method initResizeMonitor 27185 */ 27186 initResizeMonitor: function () { 27187 27188 var isGeckoWin = (UA.gecko && this.platform == "windows"); 27189 if (isGeckoWin) { 27190 // Help prevent spinning loading icon which 27191 // started with FireFox 2.0.0.8/Win 27192 var self = this; 27193 setTimeout(function(){self._initResizeMonitor();}, 0); 27194 } else { 27195 this._initResizeMonitor(); 27196 } 27197 }, 27198 27199 /** 27200 * Create and initialize the text resize monitoring iframe. 27201 * 27202 * @protected 27203 * @method _initResizeMonitor 27204 */ 27205 _initResizeMonitor : function() { 27206 27207 var oDoc, 27208 oIFrame, 27209 sHTML; 27210 27211 function fireTextResize() { 27212 Module.textResizeEvent.fire(); 27213 } 27214 27215 if (!UA.opera) { 27216 oIFrame = Dom.get("_yuiResizeMonitor"); 27217 27218 var supportsCWResize = this._supportsCWResize(); 27219 27220 if (!oIFrame) { 27221 oIFrame = document.createElement("iframe"); 27222 27223 if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && UA.ie) { 27224 oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL; 27225 } 27226 27227 if (!supportsCWResize) { 27228 // Can't monitor on contentWindow, so fire from inside iframe 27229 sHTML = ["<html><head><script ", 27230 "type=\"text/javascript\">", 27231 "window.onresize=function(){window.parent.", 27232 "YAHOO.widget.Module.textResizeEvent.", 27233 "fire();};<", 27234 "\/script></head>", 27235 "<body></body></html>"].join(''); 27236 27237 oIFrame.src = "data:text/html;charset=utf-8," + encodeURIComponent(sHTML); 27238 } 27239 27240 oIFrame.id = "_yuiResizeMonitor"; 27241 oIFrame.title = "Text Resize Monitor"; 27242 oIFrame.tabIndex = -1; 27243 oIFrame.setAttribute("role", "presentation"); 27244 27245 /* 27246 Need to set "position" property before inserting the 27247 iframe into the document or Safari's status bar will 27248 forever indicate the iframe is loading 27249 (See YUILibrary bug #1723064) 27250 */ 27251 oIFrame.style.position = "absolute"; 27252 oIFrame.style.visibility = "hidden"; 27253 27254 var db = document.body, 27255 fc = db.firstChild; 27256 if (fc) { 27257 db.insertBefore(oIFrame, fc); 27258 } else { 27259 db.appendChild(oIFrame); 27260 } 27261 27262 // Setting the background color fixes an issue with IE6/IE7, where 27263 // elements in the DOM, with -ve margin-top which positioned them 27264 // offscreen (so they would be overlapped by the iframe and its -ve top 27265 // setting), would have their -ve margin-top ignored, when the iframe 27266 // was added. 27267 oIFrame.style.backgroundColor = "transparent";
27268 27269 oIFrame.style.borderWidth = "0"; 27270 oIFrame.style.width = "2em"; 27271 oIFrame.style.height = "2em"; 27272 oIFrame.style.left = "0"; 27273 oIFrame.style.top = (-1 * (oIFrame.offsetHeight + Module.RESIZE_MONITOR_BUFFER)) + "px"; 27274 oIFrame.style.visibility = "visible"; 27275 27276 /* 27277 Don't open/close the document for Gecko like we used to, since it 27278 leads to duplicate cookies. (See YUILibrary bug #1721755) 27279 */ 27280 if (UA.webkit) { 27281 oDoc = oIFrame.contentWindow.document; 27282 oDoc.open(); 27283 oDoc.close(); 27284 } 27285 } 27286 27287 if (oIFrame && oIFrame.contentWindow) { 27288 Module.textResizeEvent.subscribe(this.onDomResize, this, true); 27289 27290 if (!Module.textResizeInitialized) { 27291 if (supportsCWResize) { 27292 if (!Event.on(oIFrame.contentWindow, "resize", fireTextResize)) { 27293 /* 27294 This will fail in IE if document.domain has 27295 changed, so we must change the listener to 27296 use the oIFrame element instead 27297 */ 27298 Event.on(oIFrame, "resize", fireTextResize); 27299 } 27300 } 27301 Module.textResizeInitialized = true; 27302 } 27303 this.resizeMonitor = oIFrame; 27304 } 27305 } 27306 }, 27307 27308 /** 27309 * Text resize monitor helper method. 27310 * Determines if the browser supports resize events on iframe content windows. 27311 * 27312 * @private 27313 * @method _supportsCWResize 27314 */ 27315 _supportsCWResize : function() { 27316 /* 27317 Gecko 1.8.0 (FF1.5), 1.8.1.0-5 (FF2) won't fire resize on contentWindow. 27318 Gecko 1.8.1.6+ (FF2.0.0.6+) and all other browsers will fire resize on contentWindow. 27319 27320 We don't want to start sniffing for patch versions, so fire textResize the same 27321 way on all FF2 flavors 27322 */ 27323 var bSupported = true; 27324 if (UA.gecko && UA.gecko <= 1.8) {
27325 bSupported = false; 27326 } 27327 return bSupported; 27328 }, 27329 27330 /** 27331 * Event handler fired when the resize monitor element is resized. 27332 * @method onDomResize 27333 * @param {DOMEvent} e The DOM resize event 27334 * @param {Object} obj The scope object passed to the handler 27335 */ 27336 onDomResize: function (e, obj) { 27337 27338 var nTop = -1 * (this.resizeMonitor.offsetHeight + Module.RESIZE_MONITOR_BUFFER); 27339 27340 this.resizeMonitor.style.top = nTop + "px"; 27341 this.resizeMonitor.style.left = "0"; 27342 }, 27343 27344 /** 27345 * Sets the Module's header content to the markup specified, or appends 27346 * the passed element to the header. 27347 * 27348 * If no header is present, one will 27349 * be automatically created. An empty string can be passed to the method 27350 * to clear the contents of the header. 27351 * 27352 * @method setHeader 27353 * @param {HTML} headerContent The markup used to set the header content. 27354 * As a convenience, non HTMLElement objects can also be passed into 27355 * the method, and will be treated as strings, with the header innerHTML 27356 * set to their default toString implementations. 27357 * 27358 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 27359 * 27360 * <em>OR</em> 27361 * @param {HTMLElement} headerContent The HTMLElement to append to 27362 * <em>OR</em> 27363 * @param {DocumentFragment} headerContent The document fragment 27364 * containing elements which are to be added to the header 27365 */ 27366 setHeader: function (headerContent) { 27367 var oHeader = this.header || (this.header = createHeader()); 27368 27369 if (headerContent.nodeName) { 27370 oHeader.innerHTML = ""; 27371 oHeader.appendChild(headerContent); 27372 } else { 27373 oHeader.innerHTML = headerContent; 27374 } 27375 27376 if (this._rendered) { 27377 this._renderHeader(); 27378 } 27379 27380 this.changeHeaderEvent.fire(headerContent); 27381 this.changeContentEvent.fire(); 27382 27383 }, 27384 27385 /** 27386 * Appends the passed element to the header. If no header is present, 27387 * one will be automatically created. 27388 * @method appendToHeader 27389 * @param {HTMLElement | DocumentFragment} element The element to 27390 * append to the header. In the case of a document fragment, the 27391 * children of the fragment will be appended to the header. 27392 */ 27393 appendToHeader: function (element) { 27394 var oHeader = this.header || (this.header = createHeader()); 27395 27396 oHeader.appendChild(element); 27397 27398 this.changeHeaderEvent.fire(element); 27399 this.changeContentEvent.fire(); 27400 27401 }, 27402 27403 /** 27404 * Sets the Module's body content to the HTML specified. 27405 * 27406 * If no body is present, one will be automatically created. 27407 * 27408 * An empty string can be passed to the method to clear the contents of the body. 27409 * @method setBody 27410 * @param {HTML} bodyContent The HTML used to set the body content 27411 * As a convenience, non HTMLElement objects can also be passed into 27412 * the method, and will be treated as strings, with the body innerHTML 27413 * set to their default toString implementations. 27414 * 27415 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 27416 * 27417 * <em>OR</em> 27418 * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only 27419 * child of the body element. 27420 * <em>OR</em> 27421 * @param {DocumentFragment} bodyContent The document fragment 27422 * containing elements which are to be added to the body 27423 */ 27424 setBody: function (bodyContent) { 27425 var oBody = this.body || (this.body = createBody()); 27426 27427 if (bodyContent.nodeName) { 27428 oBody.innerHTML = ""; 27429 oBody.appendChild(bodyContent); 27430 } else { 27431 oBody.innerHTML = bodyContent; 27432 } 27433 27434 if (this._rendered) { 27435 this._renderBody(); 27436 } 27437 27438 this.changeBodyEvent.fire(bodyContent); 27439 this.changeContentEvent.fire(); 27440 }, 27441 27442 /** 27443 * Appends the passed element to the body. If no body is present, one 27444 * will be automatically created. 27445 * @method appendToBody 27446 * @param {HTMLElement | DocumentFragment} element The element to 27447 * append to the body. In the case of a document fragment, the 27448 * children of the fragment will be appended to the body. 27449 * 27450 */ 27451 appendToBody: function (element) { 27452 var oBody = this.body || (this.body = createBody()); 27453 27454 oBody.appendChild(element); 27455 27456 this.changeBodyEvent.fire(element); 27457 this.changeContentEvent.fire(); 27458 27459 }, 27460 27461 /** 27462 * Sets the Module's footer content to the HTML specified, or appends 27463 * the passed element to the footer. If no footer is present, one will 27464 * be automatically created. An empty string can be passed to the method 27465 * to clear the contents of the footer. 27466 * @method setFooter 27467 * @param {HTML} footerContent The HTML used to set the footer 27468 * As a convenience, non HTMLElement objects can also be passed into 27469 * the method, and will be treated as strings, with the footer innerHTML 27470 * set to their default toString implementations. 27471 * 27472 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 27473 * 27474 * <em>OR</em> 27475 * @param {HTMLElement} footerContent The HTMLElement to append to 27476 * the footer 27477 * <em>OR</em> 27478 * @param {DocumentFragment} footerContent The document fragment containing 27479 * elements which are to be added to the footer 27480 */ 27481 setFooter: function (footerContent) { 27482 27483 var oFooter = this.footer || (this.footer = createFooter()); 27484 27485 if (footerContent.nodeName) { 27486 oFooter.innerHTML = ""; 27487 oFooter.appendChild(footerContent); 27488 } else { 27489 oFooter.innerHTML = footerContent; 27490 } 27491 27492 if (this._rendered) { 27493 this._renderFooter(); 27494 } 27495 27496 this.changeFooterEvent.fire(footerContent); 27497 this.changeContentEvent.fire(); 27498 }, 27499 27500 /** 27501 * Appends the passed element to the footer. If no footer is present, 27502 * one will be automatically created. 27503 * @method appendToFooter 27504 * @param {HTMLElement | DocumentFragment} element The element to 27505 * append to the footer. In the case of a document fragment, the 27506 * children of the fragment will be appended to the footer 27507 */ 27508 appendToFooter: function (element) { 27509 27510 var oFooter = this.footer || (this.footer = createFooter()); 27511 27512 oFooter.appendChild(element); 27513 27514 this.changeFooterEvent.fire(element); 27515 this.changeContentEvent.fire(); 27516 27517 }, 27518 27519 /**
27520 * Renders the Module by inserting the elements that are not already 27521 * in the main Module into their correct places. Optionally appends 27522 * the Module to the specified node prior to the render's execution. 27523 * <p> 27524 * For Modules without existing markup, the appendToNode argument 27525 * is REQUIRED. If this argument is ommitted and the current element is 27526 * not present in the document, the function will return false, 27527 * indicating that the render was a failure. 27528 * </p> 27529 * <p> 27530 * NOTE: As of 2.3.1, if the appendToNode is the document's body element 27531 * then the module is rendered as the first child of the body element, 27532 * and not appended to it, to avoid Operation Aborted errors in IE when 27533 * rendering the module before window's load event is fired. You can 27534 * use the appendtodocumentbody configuration property to change this 27535 * to append to document.body if required. 27536 * </p> 27537 * @method render 27538 * @param {String} appendToNode The element id to which the Module 27539 * should be appended to prior to rendering <em>OR</em> 27540 * @param {HTMLElement} appendToNode The element to which the Module 27541 * should be appended to prior to rendering 27542 * @param {HTMLElement} moduleElement OPTIONAL. The element that 27543 * represents the actual Standard Module container. 27544 * @return {Boolean} Success or failure of the render 27545 */ 27546 render: function (appendToNode, moduleElement) { 27547 27548 var me = this; 27549 27550 function appendTo(parentNode) { 27551 if (typeof parentNode == "string") { 27552 parentNode = document.getElementById(parentNode); 27553 } 27554 27555 if (parentNode) { 27556 me._addToParent(parentNode, me.element); 27557 me.appendEvent.fire(); 27558 } 27559 } 27560 27561 this.beforeRenderEvent.fire(); 27562 27563 if (! moduleElement) { 27564 moduleElement = this.element; 27565 } 27566 27567 if (appendToNode) { 27568 appendTo(appendToNode); 27569 } else { 27570 // No node was passed in. If the element is not already in the Dom, this fails 27571 if (! Dom.inDocument(this.element)) { 27572 YAHOO.log("Render failed. Must specify appendTo node if " + " Module isn't already in the DOM.", "error"); 27573 return false; 27574 } 27575 } 27576 27577 this._renderHeader(moduleElement); 27578 this._renderBody(moduleElement); 27579 this._renderFooter(moduleElement); 27580 27581 this._rendered = true; 27582 27583 this.renderEvent.fire(); 27584 return true; 27585 }, 27586 27587 /** 27588 * Renders the currently set header into it's proper position under the 27589 * module element. If the module element is not provided, "this.element" 27590 * is used. 27591 * 27592 * @method _renderHeader 27593 * @protected 27594 * @param {HTMLElement} moduleElement Optional. A reference to the module element 27595 */ 27596 _renderHeader: function(moduleElement){ 27597 moduleElement = moduleElement || this.element; 27598 27599 // Need to get everything into the DOM if it isn't already 27600 if (this.header && !Dom.inDocument(this.header)) { 27601 // There is a header, but it's not in the DOM yet. Need to add it. 27602 var firstChild = moduleElement.firstChild; 27603 if (firstChild) { 27604 moduleElement.insertBefore(this.header, firstChild); 27605 } else { 27606 moduleElement.appendChild(this.header); 27607 } 27608 } 27609 }, 27610 27611 /** 27612 * Renders the currently set body into it's proper position under the 27613 * module element. If the module element is not provided, "this.element" 27614 * is used. 27615 * 27616 * @method _renderBody 27617 * @protected 27618 * @param {HTMLElement} moduleElement Optional. A reference to the module element. 27619 */ 27620 _renderBody: function(moduleElement){ 27621 moduleElement = moduleElement || this.element; 27622 27623 if (this.body && !Dom.inDocument(this.body)) { 27624 // There is a body, but it's not in the DOM yet. Need to add it. 27625 if (this.footer && Dom.isAncestor(moduleElement, this.footer)) { 27626 moduleElement.insertBefore(this.body, this.footer); 27627 } else { 27628 moduleElement.appendChild(this.body); 27629 } 27630 } 27631 }, 27632 27633 /**
27634 * Renders the currently set footer into it's proper position under the 27635 * module element. If the module element is not provided, "this.element" 27636 * is used. 27637 * 27638 * @method _renderFooter 27639 * @protected 27640 * @param {HTMLElement} moduleElement Optional. A reference to the module element 27641 */ 27642 _renderFooter: function(moduleElement){ 27643 moduleElement = moduleElement || this.element; 27644 27645 if (this.footer && !Dom.inDocument(this.footer)) { 27646 // There is a footer, but it's not in the DOM yet. Need to add it. 27647 moduleElement.appendChild(this.footer); 27648 } 27649 }, 27650 27651 /** 27652 * Removes the Module element from the DOM, sets all child elements to null, and purges the bounding element of event listeners. 27653 * @method destroy 27654 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 27655 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 27656 */ 27657 destroy: function (shallowPurge) { 27658 27659 var parent, 27660 purgeChildren = !(shallowPurge); 27661 27662 if (this.element) { 27663 Event.purgeElement(this.element, purgeChildren); 27664 parent = this.element.parentNode; 27665 } 27666 27667 if (parent) { 27668 parent.removeChild(this.element); 27669 } 27670 27671 this.element = null; 27672 this.header = null; 27673 this.body = null; 27674 this.footer = null; 27675 27676 Module.textResizeEvent.unsubscribe(this.onDomResize, this); 27677 27678 this.cfg.destroy(); 27679 this.cfg = null; 27680 27681 this.destroyEvent.fire(); 27682 }, 27683 27684 /** 27685 * Shows the Module element by setting the visible configuration 27686 * property to true. Also fires two events: beforeShowEvent prior to 27687 * the visibility change, and showEvent after. 27688 * @method show 27689 */ 27690 show: function () { 27691 this.cfg.setProperty("visible", true); 27692 }, 27693 27694 /** 27695 * Hides the Module element by setting the visible configuration 27696 * property to false. Also fires two events: beforeHideEvent prior to 27697 * the visibility change, and hideEvent after. 27698 * @method hide 27699 */ 27700 hide: function () { 27701 this.cfg.setProperty("visible", false); 27702 }, 27703 27704 // BUILT-IN EVENT HANDLERS FOR MODULE // 27705 /** 27706 * Default event handler for changing the visibility property of a 27707 * Module. By default, this is achieved by switching the "display" style 27708 * between "block" and "none". 27709 * This method is responsible for firing showEvent and hideEvent. 27710 * @param {String} type The CustomEvent type (usually the property name) 27711 * @param {Object[]} args The CustomEvent arguments. For configuration 27712 * handlers, args[0] will equal the newly applied value for the property. 27713 * @param {Object} obj The scope object. For configuration handlers, 27714 * this will usually equal the owner. 27715 * @method configVisible 27716 */ 27717 configVisible: function (type, args, obj) { 27718 var visible = args[0]; 27719 if (visible) { 27720 if(this.beforeShowEvent.fire()) { 27721 Dom.setStyle(this.element, "display", "block"); 27722 this.showEvent.fire(); 27723 } 27724 } else { 27725 if (this.beforeHideEvent.fire()) { 27726 Dom.setStyle(this.element, "display", "none"); 27727 this.hideEvent.fire(); 27728 } 27729 } 27730 }, 27731 27732 /** 27733 * Default event handler for the "effect" configuration property 27734 * @param {String} type The CustomEvent type (usually the property name) 27735 * @param {Object[]} args The CustomEvent arguments. For configuration 27736 * handlers, args[0] will equal the newly applied value for the property. 27737 * @param {Object} obj The scope object. For configuration handlers, 27738 * this will usually equal the owner. 27739 * @method configEffect 27740 */ 27741 configEffect: function (type, args, obj) { 27742 this._cachedEffects = (this.cacheEffects) ? this._createEffe
27742cts(args[0]) : null; 27743 }, 27744 27745 /** 27746 * If true, ContainerEffects (and Anim instances) are cached when "effect" is set, and reused. 27747 * If false, new instances are created each time the container is hidden or shown, as was the 27748 * behavior prior to 2.9.0. 27749 * 27750 * @property cacheEffects 27751 * @since 2.9.0 27752 * @default true 27753 * @type boolean 27754 */ 27755 cacheEffects : true, 27756 27757 /** 27758 * Creates an array of ContainerEffect instances from the provided configs 27759 * 27760 * @method _createEffects 27761 * @param {Array|Object} effectCfg An effect configuration or array of effect configurations 27762 * @return {Array} An array of ContainerEffect instances. 27763 * @protected 27764 */ 27765 _createEffects: function(effectCfg) { 27766 var effectInstances = null, 27767 n, 27768 i, 27769 eff; 27770 27771 if (effectCfg) { 27772 if (effectCfg instanceof Array) { 27773 effectInstances = []; 27774 n = effectCfg.length; 27775 for (i = 0; i < n; i++) { 27776 eff = effectCfg[i]; 27777 if (eff.effect) { 27778 effectInstances[effectInstances.length] = eff.effect(this, eff.duration); 27779 } 27780 } 27781 } else if (effectCfg.effect) { 27782 effectInstances = [effectCfg.effect(this, effectCfg.duration)]; 27783 } 27784 } 27785 27786 return effectInstances; 27787 }, 27788 27789 /** 27790 * Default event handler for the "monitorresize" configuration property 27791 * @param {String} type The CustomEvent type (usually the property name) 27792 * @param {Object[]} args The CustomEvent arguments. For configuration 27793 * handlers, args[0] will equal the newly applied value for the property. 27794 * @param {Object} obj The scope object. For configuration handlers, 27795 * this will usually equal the owner. 27796 * @method configMonitorResize 27797 */ 27798 configMonitorResize: function (type, args, obj) { 27799 var monitor = args[0]; 27800 if (monitor) { 27801 this.initResizeMonitor(); 27802 } else { 27803 Module.textResizeEvent.unsubscribe(this.onDomResize, this, true); 27804 this.resizeMonitor = null; 27805 } 27806 }, 27807 27808 /** 27809 * This method is a protected helper, used when constructing the DOM structure for the module 27810 * to account for situations which may cause Operation Aborted errors in IE. It should not 27811 * be used for general DOM construction. 27812 * <p> 27813 * If the parentNode is not document.body, the element is appended as the last element. 27814 * </p> 27815 * <p> 27816 * If the parentNode is document.body the element is added as the first child to help 27817 * prevent Operation Aborted errors in IE. 27818 * </p> 27819 * 27820 * @param {parentNode} The HTML element to which the element will be added 27821 * @param {element} The HTML element to be added to parentNode's children 27822 * @method _addToParent 27823 * @protected 27824 */ 27825 _addToParent: function(parentNode, element) { 27826 if (!this.cfg.getProperty("appendtodocumentbody") && parentNode === document.body && parentNode.firstChild) { 27827 parentNode.insertBefore(element, parentNode.firstChild); 27828 } else { 27829 parentNode.appendChild(element); 27830 } 27831 }, 27832 27833 /** 27834 * Returns a String representation of the Object. 27835 * @method toString 27836 * @return {String} The string representation of the Module 27837 */ 27838 toString: function () { 27839 return "Module " + this.id; 27840 } 27841 }; 27842 27843 YAHOO.lang.augmentProto(Module, YAHOO.util.EventProvider); 27844 27845}()); 27846(function () { 27847 27848 /** 27849 * Overlay is a Module that is absolutely positioned above the page flow. It 27850 * has convenience methods for positioning and sizing, as well as options for 27851 * controlling zIndex and constraining the Overlay's position to the current 27852 * visible viewport. Overlay also contains a dynamicly generated IFRAME which 27853 * is placed beneath it for Internet Explorer 6 and 5.x so that it will be 27854 * properly rendered above SELECT elements. 27855 * @namespace YAHOO.widget 27856 * @class Overlay 27857 * @extends YAHOO.widget.Module 27858 * @param {String} el The element ID representing the Overlay <em>OR</em> 27859 * @param {HTMLElement} el The element representing the Overlay 27860 * @param {Object} userConfig The configuration object literal containing 27861 * the configuration that should be set for this Overlay. See configuration 27862 * documentation for more details. 27863 * @constructor 27864 */ 27865 YAHOO.widget.Overlay = function (el, userConfig) { 27866 YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig); 27867 }; 27868 27869 var Lang = YAHOO.lang, 27870 CustomEvent = YAHOO.util.CustomEvent, 27871 Module = YAHOO.widget.Module, 27872 Event = YAHOO.util.Event, 27873 Dom = YAHOO.util.Dom, 27874 Config = YAHOO.util.Config, 27875 UA = YAHOO.env.ua, 27876 Overlay = YAHOO.widget.Overlay, 27877 27878 _SUBSCRIBE = "subscribe", 27879 _UNSUBSCRIBE = "unsubscribe", 27880 _CONTAINED = "contained", 27881 27882 m_oIFrameTemplate, 27883 27884 /** 27885 * Constant representing the name of the Overlay's events 27886 * @property EVENT_TYPES 27887 * @private 27888 * @final 27889 * @type Object 27890 */ 27891 EVENT_TYPES = { 27892 "BEFORE_MOVE": "beforeMove", 27893 "MOVE": "move" 27894 }, 27895 27896 /** 27897 * Constant representing the Overlay's configuration properties 27898 * @property DEFAULT_CONFIG 27899 * @private 27900 * @final 27901 * @type Object 27902 */ 27903 DEFAULT_CONFIG = { 27904 27905 "X": { 27906 key: "x", 27907 validator: Lang.isNumber, 27908 suppressEvent: true, 27909 supercedes: ["iframe"] 27910 }, 27911 27912 "Y": { 27913 key: "y", 27914 validator: Lang.isNumber, 27915 suppressEvent: true, 27916 supercedes: ["iframe"] 27917 }, 27918 27919 "XY": { 27920 key: "xy", 27921 suppressEvent: true, 27922 supercedes: ["iframe"] 27923 }, 27924 27925 "CONTEXT": { 27926 key: "context", 27927 suppressEvent: true, 27928 supercedes: ["iframe"] 27929 }, 27930 27931 "FIXED_CENTER": { 27932 key: "fixedcenter", 27933 value: false, 27934 supercedes: ["iframe", "visible"] 27935 }, 27936 27937 "WIDTH": { 27938 key: "width", 27939 suppressEvent: true, 27940 supercedes: ["context", "fixedcenter", "iframe"] 27941 }, 27942 27943 "HEIGHT": { 27944 key: "height", 27945 suppressEvent: true, 27946 supercedes: ["context", "fixedcenter", "iframe"] 27947 }, 27948 27949 "AUTO_FILL_HEIGHT" : { 27950 key: "autofillheight", 27951 supercedes: ["height"], 27952 value:"body" 27953 }, 27954 27955 "ZINDEX": { 27956 key: "zindex", 27957 value: null 27958 }, 27959 27960 "CONSTRAIN_TO_VIEWPORT": { 27961 key: "constraintoviewport", 27962 value: false, 27963 validator: Lang.isBoolean, 27964 supercedes: ["iframe", "x", "y", "xy"] 27965 }, 27966 27967 "IFRAME": { 27968 key: "iframe", 27969 value: (UA.ie == 6 ? true : false), 27970 validator: Lang.isBoolean, 27971 supercedes: ["zindex"] 27972 }, 27973 27974 "PREVENT_CONTEXT_OVERLAP": { 27975 key: "preventcontextoverlap", 27976 value: false, 27977 validator: Lang.isBoolean, 27978 supercedes: ["constraintoviewport"] 27979 } 27980 27981 }; 27982 27983 /** 27984 * The URL that will be placed in the iframe 27985 * @property YAHOO.widget.Overlay.IFRAME_SRC 27986 * @static 27987 * @final
27988 * @type String 27989 */ 27990 Overlay.IFRAME_SRC = "javascript:false;"; 27991 27992 /** 27993 * Number representing how much the iframe shim should be offset from each 27994 * side of an Overlay instance, in pixels. 27995 * @property YAHOO.widget.Overlay.IFRAME_SRC 27996 * @default 3 27997 * @static 27998 * @final 27999 * @type Number 28000 */ 28001 Overlay.IFRAME_OFFSET = 3; 28002 28003 /** 28004 * Number representing the minimum distance an Overlay instance should be 28005 * positioned relative to the boundaries of the browser's viewport, in pixels. 28006 * @property YAHOO.widget.Overlay.VIEWPORT_OFFSET 28007 * @default 10 28008 * @static 28009 * @final 28010 * @type Number 28011 */ 28012 Overlay.VIEWPORT_OFFSET = 10; 28013 28014 /** 28015 * Constant representing the top left corner of an element, used for 28016 * configuring the context element alignment 28017 * @property YAHOO.widget.Overlay.TOP_LEFT 28018 * @static 28019 * @final 28020 * @type String 28021 */ 28022 Overlay.TOP_LEFT = "tl"; 28023 28024 /** 28025 * Constant representing the top right corner of an element, used for 28026 * configuring the context element alignment 28027 * @property YAHOO.widget.Overlay.TOP_RIGHT 28028 * @static 28029 * @final
28030 * @type String 28031 */ 28032 Overlay.TOP_RIGHT = "tr"; 28033 28034 /** 28035 * Constant representing the top bottom left corner of an element, used for 28036 * configuring the context element alignment 28037 * @property YAHOO.widget.Overlay.BOTTOM_LEFT 28038 * @static 28039 * @final 28040 * @type String 28041 */ 28042 Overlay.BOTTOM_LEFT = "bl"; 28043 28044 /** 28045 * Constant representing the bottom right corner of an element, used for 28046 * configuring the context element alignment 28047 * @property YAHOO.widget.Overlay.BOTTOM_RIGHT 28048 * @static 28049 * @final 28050 * @type String 28051 */ 28052 Overlay.BOTTOM_RIGHT = "br"; 28053 28054 Overlay.PREVENT_OVERLAP_X = { 28055 "tltr": true, 28056 "blbr": true, 28057 "brbl": true, 28058 "trtl": true 28059 }; 28060 28061 Overlay.PREVENT_OVERLAP_Y = { 28062 "trbr": true, 28063 "tlbl": true, 28064 "bltl": true, 28065 "brtr": true 28066 }; 28067 28068 /** 28069 * Constant representing the default CSS class used for an Overlay 28070 * @property YAHOO.widget.Overlay.CSS_OVERLAY 28071 * @static 28072 * @final 28073 * @type Str
28073ing 28074 */ 28075 Overlay.CSS_OVERLAY = "yui-overlay"; 28076 28077 /** 28078 * Constant representing the default hidden CSS class used for an Overlay. This class is 28079 * applied to the overlay's outer DIV whenever it's hidden. 28080 * 28081 * @property YAHOO.widget.Overlay.CSS_HIDDEN 28082 * @static 28083 * @final 28084 * @type String 28085 */ 28086 Overlay.CSS_HIDDEN = "yui-overlay-hidden"; 28087 28088 /** 28089 * Constant representing the default CSS class used for an Overlay iframe shim. 28090 * 28091 * @property YAHOO.widget.Overlay.CSS_IFRAME 28092 * @static 28093 * @final 28094 * @type String 28095 */ 28096 Overlay.CSS_IFRAME = "yui-overlay-iframe"; 28097 28098 /** 28099 * Constant representing the names of the standard module elements 28100 * used in the overlay. 28101 * @property YAHOO.widget.Overlay.STD_MOD_RE 28102 * @static 28103 * @final 28104 * @type RegExp 28105 */ 28106 Overlay.STD_MOD_RE = /^\s*?(body|footer|header)\s*?$/i; 28107 28108 /** 28109 * A singleton CustomEvent used for reacting to the DOM event for 28110 * window scroll 28111 * @event YAHOO.widget.Overlay.windowScrollEvent 28112 */ 28113 Overlay.windowScrollEvent = new CustomEvent("windowScroll"); 28114 28115 /** 28116 * A singleton CustomEvent used for reacting to the DOM event for 28117 * window resize 28118 * @event YAHOO.widget.Overlay.windowResizeEvent 28119 */ 28120 Overlay.windowResizeEvent = new CustomEvent("windowResize"); 28121 28122 /** 28123 * The DOM event handler used to fire the CustomEvent for window scroll 28124 * @method YAHOO.widget.Overlay.windowScrollHandler 28125 * @static 28126 * @param {DOMEvent} e The DOM scroll event 28127 */ 28128 Overlay.windowScrollHandler = function (e) { 28129 var t = Event.getTarget(e); 28130 28131 // - Webkit (Safari 2/3) and Opera 9.2x bubble scroll events from elements to window 28132 // - FF2/3 and IE6/7, Opera 9.5x don't bubble scroll events from elements to window 28133 // - IE doesn't recognize scroll registered on the document. 28134 // 28135 // Also, when document view is scrolled, IE doesn't provide a target, 28136 // rest of the browsers set target to window.document, apart from opera 28137 // which sets target to window. 28138 if (!t || t === window || t === window.document) { 28139 if (UA.ie) { 28140 28141 if (! window.scrollEnd) { 28142 window.scrollEnd = -1; 28143 } 28144 28145 clearTimeout(window.scrollEnd); 28146 28147 window.scrollEnd = setTimeout(function () { 28148 Overlay.windowScrollEvent.fire(); 28149 }, 1); 28150 28151 } else { 28152 Overlay.windowScrollEvent.fire(); 28153 } 28154 } 28155 }; 28156 28157 /** 28158 * The DOM event handler used to fire the CustomEvent for window resize 28159 * @method YAHOO.widget.Overlay.windowResizeHandler 28160 * @static 28161 * @param {DOMEvent} e The DOM resize event 28162 */ 28163 Overlay.windowResizeHandler = function (e) { 28164 28165 if (UA.ie) { 28166 if (! window.resizeEnd) { 28167 window.resizeEnd = -1; 28168 } 28169 28170 clearTimeout(window.resizeEnd); 28171 28172 window.resizeEnd = setTimeout(function () { 28173 Overlay.windowResizeEvent.fire(); 28174 }, 100); 28175 } else { 28176 Overlay.windowResizeEvent.fire(); 28177 } 28178 }; 28179 28180 /** 28181 * A boolean that indicated whether the window resize and scroll events have 28182 * already been subscribed to. 28183 * @property YAHOO.widget.Overlay._initialized 28184 * @private 28185 * @type Boolean 28186 */ 28187 Overlay._initialized = null; 28188 28189 if (Overlay._initialized === null) { 28190 Event.on(window, "scroll", Overlay.windowScrollHandler); 28191 Event.on(window, "resize", Overlay.windowResizeHandler); 28192 Overlay._initialized = true; 28193 } 28194 28195 /** 28196 * Internal map of special event types, which are provided 28197 * by the instance. It maps the event type to the custom event 28198 * instance. Contains entries for the "windowScroll", "windowResize" and 28199 * "textResize" static container events. 28200 * 28201 * @property YAHOO.widget.Overlay._TRIGGER_MAP 28202 * @type Object 28203 * @static 28204 * @private 28205 */ 28206 Overlay._TRIGGER_MAP = { 28207 "windowScroll" : Overlay.windowScrollEvent, 28208 "windowResize" : Overlay.windowResizeEvent, 28209 "textResize" : Module.textResizeEvent 28210 }; 28211 28212 YAHOO.extend(Overlay, Module, { 28213 28214 /** 28215 * <p> 28216 * Array of default event types which will trigger 28217 * context alignment for the Overlay class. 28218 * </p> 28219 * <p>The array is empty by default for Overlay, 28220 * but maybe populated in future releases, so classes extending 28221 * Overlay which need to define their own set of CONTEXT_TRIGGERS 28222 * should concatenate their super class's prototype.CONTEXT_TRIGGERS 28223 * value with their own array of values. 28224 * </p> 28225 * <p> 28226 * E.g.: 28227 * <code>CustomOverlay.prototype.CONTEXT_TRIGGERS = YAHOO.widget.Overlay.prototype.CONTEXT_TRIGGERS.concat(["windowScroll"]);</code> 28228 * </p> 28229 * 28230 * @property CONTEXT_TRIGGERS 28231 * @type Array 28232 * @final 28233 */ 28234 CONTEXT_TRIGGERS : [], 28235 28236 /** 28237 * The Overlay initialization method, which is executed for Overlay and 28238 * all of its subclasses. This method is automatically called by the 28239 * constructor, and sets up all DOM references for pre-existing markup, 28240 * and creates required markup if it is not already present. 28241 * @method init 28242 * @param {String} el The element ID representing the Overlay <em>OR</em> 28243 * @param {HTMLElement} el The element representing the Overlay 28244 * @param {Object} userConfig The configuration object literal 28245 * containing the configuration that should be set for this Overlay. 28246 * See configuration documentation for more details. 28247 */ 28248 init: function (el, userConfig) { 28249 28250 /* 28251 Note that we don't pass the user config in here yet because we 28252 only want it executed once, at the lowest subclass level 28253 */ 28254 28255 Overlay.superclass.init.call(this, el/*, userConfig*/); 28256 28257 this.beforeInitEvent.fire(Overlay); 28258 28259 Dom.addClass(this.element, Overlay.CSS_OVERLAY); 28260 28261 if (userConfig) { 28262 this.cfg.applyConfig(userConfig, true); 28263 } 28264 28265 if (this.platform == "mac" && UA.gecko) { 28266 28267 if (! Config.alreadySubscribed(this.showEvent, 28268 this.showMacGeckoScrollbars, this)) { 28269 28270 this.showEvent.subscribe(this.showMacGeckoScrollbars, 28271 this, true); 28272 28273 } 28274 28275 if (! Config.alreadySubscribed(this.hideEvent, 28276 this.hideMacGeckoScrollbars, this)) { 28277 28278 this.hideEvent.subscribe(this.hideMacGeckoScrollbars, 28279 this, true); 28280 28281 } 28282 } 28283 28284 this.initEvent.fire(Overlay); 28285 }, 28286 28287 /** 28288 * Initializes the custom events for Overlay which are fired 28289 * automatically at appropriate times by the Overlay class. 28290 * @method initEvents 28291 */ 28292 initEvents: function () { 28293 28294 Overlay.superclass.initEvents.call(this); 28295 28296 var SIGNATURE = CustomEvent.LIST; 28297 28298 /** 28299 * CustomEvent fired before the Overlay is moved. 28300 * @event beforeMoveEvent 28301 * @param {Number} x x coordinate 28302 * @param {Number} y y coordinate 28303 */ 28304 this.beforeMoveEvent = this.createEvent(EVENT_TYPES.BEFORE_MOVE); 28305 this.beforeMoveEvent.signature = SIGNATURE; 28306 28307 /** 28308 * CustomEvent fired after the Overlay is moved. 28309 * @event moveEvent 28310 * @param {Number} x x coordinate 28311 * @param {Number} y y coordinate 28312 */ 28313 this.moveEvent = this.createEvent(EVENT_TYPES.MOVE); 28314 this.moveEvent.signature = SIGNATURE; 28315 28316 }, 28317 28318 /** 28319 * Initializes the class's configurable properties which can be changed 28320 * using the Overlay's Config object (cfg). 28321 * @method initDefaultConfig 28322 */ 28323 initDefaultConfig: function () { 28324 28325 Overlay.superclass.initDefaultConfig.call(this); 28326 28327 var cfg = this.cfg; 28328 28329 // Add overlay config properties // 28330 28331 /** 28332 * The absolute x-coordinate position of the Overlay 28333 * @config x 28334 * @type Number 28335 * @default null 28336 */ 28337 cfg.addProperty(DEFAULT_CONFIG.X.key, { 28338 28339 handler: this.configX,
28340 validator: DEFAULT_CONFIG.X.validator, 28341 suppressEvent: DEFAULT_CONFIG.X.suppressEvent, 28342 supercedes: DEFAULT_CONFIG.X.supercedes 28343 28344 }); 28345 28346 /** 28347 * The absolute y-coordinate position of the Overlay 28348 * @config y 28349 * @type Number 28350 * @default null 28351 */ 28352 cfg.addProperty(DEFAULT_CONFIG.Y.key, { 28353 28354 handler: this.configY, 28355 validator: DEFAULT_CONFIG.Y.validator, 28356 suppressEvent: DEFAULT_CONFIG.Y.suppressEvent, 28357 supercedes: DEFAULT_CONFIG.Y.supercedes 28358 28359 }); 28360 28361 /** 28362 * An array with the absolute x and y positions of the Overlay 28363 * @config xy 28364 * @type Number[] 28365 * @default null 28366 */ 28367 cfg.addProperty(DEFAULT_CONFIG.XY.key, { 28368 handler: this.configXY, 28369 suppressEvent: DEFAULT_CONFIG.XY.suppressEvent, 28370 supercedes: DEFAULT_CONFIG.XY.supercedes 28371 }); 28372 28373 /** 28374 * <p> 28375 * The array of context arguments for context-sensitive positioning. 28376 * </p> 28377 * 28378 * <p> 28379 * The format of the array is: <code>[contextElementOrId, overlayCorner, contextCorner, arrayOfTriggerEvents (optional), xyOffset
28379(optional)]</code>, the 28380 * the 5 array elements described in detail below: 28381 * </p> 28382 * 28383 * <dl> 28384 * <dt>contextElementOrId <String|HTMLElement></dt> 28385 * <dd>A reference to the context element to which the overlay should be aligned (or it's id).</dd> 28386 * <dt>overlayCorner <String></dt> 28387 * <dd>The corner of the overlay which is to be used for alignment. This corner will be aligned to the 28388 * corner of the context element defined by the "contextCorner" entry which follows. Supported string values are: 28389 * "tr" (top right), "tl" (top left), "br" (bottom right), or "bl" (bottom left).</dd> 28390 * <dt>contextCorner <String></dt> 28391 * <dd>The corner of the context element which is to be used for alignment. Supported string values are the same ones listed for the "overlayCorner" entry above.</dd> 28392 * <dt>arrayOfTriggerEvents (optional) <Array[String|CustomEvent]></dt> 28393 * <dd> 28394 * <p> 28395 * By default, context alignment is a one time operation, aligning the Overlay to the context element when context configuration property is set, or when the <a href="#method_align">align</a> 28396 * method is invoked. However, you can use the optional "arrayOfTriggerEvents" entry to define the list of events which should force the overlay to re-align itself with the context element. 28397 * This is useful in situations where the layout of the document may change, resulting in the context element's position being modified. 28398 * </p> 28399 * <p> 28400 * The array can contain either event type strings for events the instance publishes (e.g. "beforeShow") or CustomEvent instances. Additionally the following 28401 * 3 static container event types are also currently supported : <code>"windowResize", "windowScroll", "textResize"</code> (defined in <a href="#property__TRIGGER_MAP">_TRIGGER_MAP</a> private property). 28402 * </p> 28403 * </dd> 28404 * <dt>xyOffset <Number[]></dt> 28405 * <dd> 28406 * A 2 element Array specifying the X and Y pixel amounts by which the Overlay should be offset from the aligned corner. e.g. [5,0] offsets the Overlay 5 pixels to the left, <em>after</em> aligning the given context corners. 28407 * NOTE: If using this property and no triggers need to be defined, the arrayOfTriggerEvents property should be set to null to maintain correct array positions for the arguments. 28408 * </dd> 28409 * </dl> 28410 * 28411 * <p> 28412 * For example, setting this property to <code>["img1", "tl", "bl"]</code> will 28413 * align the Overlay's top left corner to the bottom left corner of the 28414 * context element with id "img1". 28415 * </p> 28416 * <p> 28417 * Setting this property to <code>["img1", "tl", "bl", null, [0,5]</code> will 28418 * align the Overlay's top left corner to the bottom left corner of the 28419 * context element with id "img1", and then offset it by 5 pixels on the Y axis (providing a 5 pixel gap between the bottom of the context element and top of the overlay). 28420 * </p> 28421 * <p> 28422 * Adding the optional trigger values: <code>["img1", "tl", "bl", ["beforeShow", "windowResize"], [0,5]]</code>, 28423 * will re-align the overlay position, whenever the "beforeShow" or "windowResize" events are fired. 28424 * </p> 28425 * 28426 * @config context 28427 * @type Array 28428 * @default null 28429 */ 28430 cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key, { 28431 handler: this.configContext, 28432 suppressEvent: DEFAULT_CONFIG.CONTEXT.suppressEvent, 28433 supercedes: DEFAULT_CONFIG.CONTEXT.supercedes 28434 }); 28435 28436 /** 28437 * Determines whether or not the Overlay should be anchored 28438 * to the center of the viewport. 28439 * 28440 * <p>This property can be set to:</p> 28441 * 28442 * <dl> 28443 * <dt>true</dt> 28444 * <dd> 28445 * To enable fixed center positioning 28446 * <p> 28447 * When enabled, the overlay will 28448 * be positioned in the center of viewport when initially displayed, and 28449 * will remain in the center of the viewport whenever the window is 28450 * scrolled or resized. 28451 * </p> 28452 * <p> 28453 * If the overlay is too big for the viewport, 28454 * it's top left corner will be aligned with the top left corner of the viewport. 28455 * </p> 28456 * </dd> 28457 * <dt>false</dt> 28458 * <dd> 28459 * To disable fixed center positioning. 28460 * <p>In this case the overlay can still be 28461 * centered as a one-off operation, by invoking the <code>center()</code> method, 28462 * however it will not remain centered when the window is scrolled/resized. 28463 * </dd> 28464 * <dt>"contained"<dt> 28465 * <dd>To enable fixed center positioning, as with the <code>true</code> option. 28466 * <p>However, unlike setting the property to <code>true</code>, 28467 * when the property is set to <code>"contained"</code>, if the overlay is 28468 * too big for the viewport, it will not get automatically centered when the 28469 * user scrolls or resizes the window (until the window is large enough to contain the 28470 * overlay). This is useful in cases where the Overlay has both header and footer
28471 * UI controls which the user may need to access. 28472 * </p> 28473 * </dd> 28474 * </dl> 28475 * 28476 * @config fixedcenter 28477 * @type Boolean | String 28478 * @default false 28479 */ 28480 cfg.addProperty(DEFAULT_CONFIG.FIXED_CENTER.key, { 28481 handler: this.configFixedCenter, 28482 value: DEFAULT_CONFIG.FIXED_CENTER.value, 28483 validator: DEFAULT_CONFIG.FIXED_CENTER.validator, 28484 supercedes: DEFAULT_CONFIG.FIXED_CENTER.supercedes 28485 }); 28486 28487 /** 28488 * CSS width of the Overlay. 28489 * @config width 28490 * @type String 28491 * @default null 28492 */ 28493 cfg.addProperty(DEFAULT_CONFIG.WIDTH.key, { 28494 handler: this.configWidth, 28495 suppressEvent: DEFAULT_CONFIG.WIDTH.suppressEvent, 28496 supercedes: DEFAULT_CONFIG.WIDTH.supercedes 28497 }); 28498 28499 /** 28500 * CSS height of the Overlay. 28501 * @config height
28502 * @type String 28503 * @default null 28504 */ 28505 cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key, { 28506 handler: this.configHeight, 28507 suppressEvent: DEFAULT_CONFIG.HEIGHT.suppressEvent, 28508 supercedes: DEFAULT_CONFIG.HEIGHT.supercedes 28509 }); 28510 28511 /** 28512 * Standard module element which should auto fill out the height of the Overlay if the height config property is set. 28513 * Supported values are "header", "body", "footer". 28514 * 28515 * @config autofillheight 28516 * @type String 28517 * @default null 28518 */ 28519 cfg.addProperty(DEFAULT_CONFIG.AUTO_FILL_HEIGHT.key, { 28520 handler: this.configAutoFillHeight, 28521 value : DEFAULT_CONFIG.AUTO_FILL_HEIGHT.value, 28522 validator : this._validateAutoFill, 28523 supercedes: DEFAULT_CONFIG.AUTO_FILL_HEIGHT.supercedes 28524 }); 28525 28526 /** 28527 * CSS z-index of the Overlay. 28528 * @config zIndex 28529 * @type Number 28530 * @default null 28531 */ 28532 cfg.addProperty(DEFAULT_CONFIG.ZINDEX.key, { 28533 handler: this.configzIndex, 28534 value: DEFAULT_CONFIG.ZINDEX.value 28535 }); 28536 28537 /** 28538 * True if the Overlay should be prevented from being positioned 28539 * out of the viewport. 28540 * @config constraintoviewport 28541 * @type Boolean 28542 * @default false 28543 */ 28544 cfg.addProperty(DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.key, { 28545 28546 handler: this.configConstrainToViewport, 28547 value: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.value, 28548 validator: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.validator, 28549 supercedes: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.supercedes 28550 28551 }); 28552 28553 /** 28554 * @config iframe 28555 * @description Boolean indicating whether or not the Overlay should 28556 * have an IFRAME shim; used to prevent SELECT elements from 28557 * poking through an Overlay instance in IE6. When set to "true", 28558 * the iframe shim is created when the Overlay instance is intially 28559 * made visible. 28560 * @type Boolean 28561 * @default true for IE6 and below, false for all other browsers. 28562 */ 28563 cfg.addProperty(DEFAULT_CONFIG.IFRAME.key, { 28564 28565 handler: this.configIframe, 28566 value: DEFAULT_CONFIG.IFRAME.value, 28567 validator: DEFAULT_CONFIG.IFRAME.validator, 28568 supercedes: DEFAULT_CONFIG.IFRAME.supercedes 28569 28570 }); 28571 28572 /** 28573 * @config preventcontextoverlap 28574 * @description Boolean indicating whether or not the Overlay should overlap its 28575 * context element (defined using the "context" configuration property) when the 28576 * "constraintoviewport" configuration property is set to "true". 28577 * @type Boolean 28578 * @default false 28579 */ 28580 cfg.addProperty(DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.key, { 28581 value: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.value, 28582 validator: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.validator, 28583 supercedes: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.supercedes 28584 }); 28585 }, 28586 28587 /** 28588 * Moves the Overlay to the specified position. This function is 28589 * identical to calling this.cfg.setProperty("xy", [x,y]); 28590 * @method moveTo 28591 * @param {Number} x The Overlay's new x position 28592 * @param {Number} y The Overlay's new y position 28593 */ 28594 moveTo: function (x, y) { 28595 this.cfg.setProperty("xy", [x, y]); 28596 }, 28597 28598 /** 28599 * Adds a CSS class ("hide-scrollbars") and removes a CSS class 28600 * ("show-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 28601 * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) 28602 * @method hideMacGeckoScrollbars 28603 */ 28604 hideMacGeckoScrollbars: function () { 28605 Dom.replaceClass(this.element, "show-scrollbars", "hide-scrollbars"); 28606 }, 28607 28608 /** 28609 * Adds a CSS class ("show-scrollbars") and removes a CSS class 28610 * ("hide-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 28611 * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) 28612 * @method showMacGeckoScrollbars 28613 */ 28614 showMacGeckoScrollbars: function () { 28615 Dom.replaceClass(this.element, "hide-scrollbars", "show-scrollbars"); 28616 }, 28617 28618 /** 28619 * Internal implementation to set the visibility of the overlay in the DOM. 28620 * 28621 * @method _setDomVisibility 28622 * @param {boolean} visible Whether to show or hide the Overlay's outer element 28623 * @protected 28624 */ 28625 _setDomVisibility : function(show) { 28626 Dom.setStyle(this.element, "visibility", (show) ? "visible" : "hidden"); 28627 var hiddenClass = Overlay.CSS_HIDDEN; 28628 28629 if (show) { 28630 Dom.removeClass(this.element, hiddenClass); 28631 } else { 28632 Dom.addClass(this.element, hiddenClass); 28633 } 28634 }, 28635 28636 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 28637 /** 28638 * The default event handler fired when the "visible" property is 28639 * changed. This method is responsible for firing showEvent 28640 * and hideEvent. 28641 * @method configVisible 28642 * @param {String} type The CustomEvent type (usually the property name) 28643 * @param {Object[]} args The CustomEvent arguments. For configuration 28644 * handlers, args[0] will equal the newly applied value for the property. 28645 * @param {Object} obj The scope object. For configuration handlers, 28646 * this will usually equal the owner. 28647 */ 28648 configVisible: function (type, args, obj) { 28649 28650 var visible = args[0], 28651 currentVis = Dom.getStyle(this.element, "visibility"), 28652 effects = this._cachedEffects || this._createEffe
28652cts(this.cfg.getProperty("effect")), 28653 isMacGecko = (this.platform == "mac" && UA.gecko), 28654 alreadySubscribed = Config.alreadySubscribed, 28655 ei, e, j, k, h, 28656 nEffectInstances; 28657 28658 if (currentVis == "inherit") { 28659 e = this.element.parentNode; 28660 28661 while (e.nodeType != 9 && e.nodeType != 11) { 28662 currentVis = Dom.getStyle(e, "visibility"); 28663 28664 if (currentVis != "inherit") { 28665 break; 28666 } 28667 28668 e = e.parentNode; 28669 } 28670 28671 if (currentVis == "inherit") { 28672 currentVis = "visible"; 28673 } 28674 } 28675 28676 if (visible) { // Show 28677 28678 if (isMacGecko) { 28679 this.showMacGeckoScrollbars(); 28680 } 28681 28682 if (effects) { // Animate in 28683 if (visible) { // Animate in if not showing 28684 28685 // Fading out is a bit of a hack, but didn't want to risk doing 28686 // something broader (e.g a generic this._animatingOut) for 2.9.0 28687 28688 if (currentVis != "visible" || currentVis === "" || this._fadingOut) { 28689 if (this.beforeShowEvent.fire()) { 28690 28691 nEffectInstances = effects.length; 28692 28693 for (j = 0; j < nEffectInstances; j++) { 28694 ei = effects[j]; 28695 if (j === 0 && !alreadySubscribed(ei.animateInCompleteEvent, this.showEvent.fire, this.showEvent)) { 28696 ei.animateInCompleteEvent.subscribe(this.showEvent.fire, this.showEvent, true); 28697 } 28698 ei.animateIn(); 28699 } 28700 } 28701 } 28702 } 28703 } else { // Show 28704 if (currentVis != "visible" || currentVis === "") { 28705 if (this.beforeShowEvent.fire()) { 28706 this._setDomVisibility(true); 28707 this.cfg.refireEvent("iframe"); 28708 this.showEvent.fire(); 28709 } 28710 } else { 28711 this._setDomVisibility(true); 28712 } 28713 } 28714 } else { // Hide 28715 28716 if (isMacGecko) { 28717 this.hideMacGeckoScrollbars(); 28718 } 28719 28720 if (effects) { // Animate out if showing 28721 if (currentVis == "visible" || this._fadingIn) { 28722 if (this.beforeHideEvent.fire()) { 28723 nEffectInstances = effects.length; 28724 for (k = 0; k < nEffectInstances; k++) { 28725 h = effects[k]; 28726 28727 if (k === 0 && !alreadySubscribed(h.animateOutCompleteEvent, this.hideEvent.fire, this.hideEvent)) { 28728 h.animateOutCompleteEvent.subscribe(this.hideEvent.fire, this.hideEvent, true); 28729 } 28730 h.animateOut(); 28731 } 28732 } 28733 28734 } else if (currentVis === "") { 28735 this._setDomVisibility(false); 28736 } 28737 28738 } else { // Simple hide 28739 28740 if (currentVis == "visible" || currentVis === "") { 28741 if (this.beforeHideEvent.fire()) { 28742 this._setDomVisibility(false); 28743 this.hideEvent.fire(); 28744 } 28745 } else { 28746 this._setDomVisibility(false); 28747 } 28748 } 28749 } 28750 }, 28751 28752 /** 28753 * Fixed center event handler used for centering on scroll/resize, but only if 28754 * the overlay is visible and, if "fixedcenter" is set to "contained", only if 28755 * the overlay fits within the viewport. 28756 * 28757 * @method doCenterOnDOMEvent 28758 */ 28759 doCenterOnDOMEvent: function () { 28760 var cfg = this.cfg, 28761 fc = cfg.getProperty("fixedcenter"); 28762 28763 if (cfg.getProperty("visible")) { 28764 if (fc && (fc !== _CONTAINED || this.fitsInViewport())) { 28765 this.center(); 28766 } 28767 } 28768 }, 28769 28770 /** 28771 * Determines if the Overlay (including the offset value defined by Overlay.VIEWPORT_OFFSET) 28772 * will fit entirely inside the viewport, in both dimensions - width and height. 28773 * 28774 * @method fitsInViewport 28775 * @return boolean true if the Overlay will fit, false if not 28776 */ 28777 fitsInViewport : function() { 28778 var nViewportOffset = Overlay.VIEWPORT_OFFSET, 28779 element = this.element, 28780 elementWidth = element.offsetWidth, 28781 elementHeight = element.offsetHeight, 28782 viewportWidth = Dom.getViewportWidth(),
28783 viewportHeight = Dom.getViewportHeight(); 28784 28785 return ((elementWidth + nViewportOffset < viewportWidth) && (elementHeight + nViewportOffset < viewportHeight)); 28786 }, 28787 28788 /** 28789 * The default event handler fired when the "fixedcenter" property 28790 * is changed. 28791 * @method configFixedCenter 28792 * @param {String} type The CustomEvent type (usually the property name) 28793 * @param {Object[]} args The CustomEvent arguments. For configuration 28794 * handlers, args[0] will equal the newly applied value for the property. 28795 * @param {Object} obj The scope object. For configuration handlers, 28796 * this will usually equal the owner. 28797 */ 28798 configFixedCenter: function (type, args, obj) { 28799 28800 var val = args[0], 28801 alreadySubscribed = Config.alreadySubscribed, 28802 windowResizeEvent = Overlay.windowResizeEvent, 28803 windowScrollEvent = Overlay.windowScrollEvent; 28804 28805 if (val) { 28806 this.center(); 28807 28808 if (!alreadySubscribed(this.beforeShowEvent, this.center)) { 28809 this.beforeShowEvent.subscribe(this.center); 28810 } 28811 28812 if (!alreadySubscribed(windowResizeEvent, this.doCenterOnDOMEvent, this)) { 28813 windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true); 28814 } 28815 28816 if (!alreadySubscribed(windowScrollEvent, this.doCenterOnDOMEvent, this)) { 28817 windowScrollEvent.subscribe(this.doCenterOnDOMEvent, this, true); 28818 } 28819 28820 } else { 28821 this.beforeShowEvent.unsubscribe(this.center); 28822 28823 windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this); 28824 windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this); 28825 } 28826 }, 28827 28828 /** 28829 * The default event handler fired when the "height" property is changed. 28830 * @method configHeight 28831 * @param {String} type The CustomEvent type (usually the property name) 28832 * @param {Object[]} args The CustomEvent arguments. For configuration 28833 * handlers, args[0] will equal the newly applied value for the property. 28834 * @param {Object} obj The scope object. For configuration handlers, 28835 * this will usually equal the owner. 28836 */ 28837 configHeight: function (type, args, obj) { 28838 28839 var height = args[0], 28840 el = this.element; 28841 28842 Dom.setStyle(el, "height", height); 28843 this.cfg.refireEvent("iframe"); 28844 }, 28845 28846 /** 28847 * The default event handler fired when the "autofillheight" property is changed. 28848 * @method configAutoFillHeight 28849 * 28850 * @param {String} type The CustomEvent type (usually the property name) 28851 * @param {Object[]} args The CustomEvent arguments. For configuration 28852 * handlers, args[0] will equal the newly applied value for the property. 28853 * @param {Object} obj The scope object. For configuration handlers, 28854 * this will usually equal the owner. 28855 */ 28856 configAutoFillHeight: function (type, args, obj) { 28857 var fillEl = args[0], 28858 cfg = this.cfg, 28859 autoFillHeight = "autofillheight", 28860 height = "height", 28861 currEl = cfg.getProperty(autoFillHeight), 28862 autoFill = this._autoFillOnHeightChange; 28863 28864 cfg.unsubscribeFromConfigEvent(height, autoFill); 28865 Module.textResizeEvent.unsubscribe(autoFill); 28866 this.changeContentEvent.unsubscribe(autoFill); 28867 28868 if (currEl && fillEl !== currEl && this[currEl]) { 28869 Dom.setStyle(this[currEl], height, ""); 28870 } 28871 28872 if (fillEl) { 28873 fillEl = Lang.trim(fillEl.toLowerCase()); 28874 28875 cfg.subscribeToConfigEvent(height, autoFill, this[fillEl], this); 28876 Module.textResizeEvent.subscribe(autoFill, this[fillEl], this); 28877 this.changeContentEvent.subscribe(autoFill, this[fillEl], this); 28878 28879 cfg.setProperty(autoFillHeight, fillEl, true); 28880 } 28881 }, 28882 28883 /** 28884 * The default event handler fired when the "width" property is changed. 28885 * @method configWidth 28886 * @param {String} type The CustomEvent type (usually the property name) 28887 * @param {Object[]} args The CustomEvent arguments. For configuration 28888 * handlers, args[0] will equal the newly applied value for the property. 28889 * @param {Object} obj The scope object. For configuration handlers, 28890 * this will usually equal the owner. 28891 */ 28892 configWidth: function (type, args, obj) { 28893 28894 var width = args[0], 28895 el = this.element; 28896 28897 Dom.setStyle(el, "width", width); 28898 this.cfg.refireEvent("iframe"); 28899 }, 28900 28901 /** 28902 * The default event handler fired when the "zIndex" property is changed. 28903 * @method configzIndex 28904 * @param {String} type The CustomEvent type (usually the property name) 28905 * @param {Object[]} args The CustomEvent arguments. For configuration 28906 * handlers, args[0] will equal the newly applied value for the property. 28907 * @param {Object} obj The scope object. For configuration handlers, 28908 * this will usually equal the owner. 28909 */ 28910 configzIndex: function (type, args, obj) { 28911 28912 var zIndex = args[0], 28913 el = this.element; 28914 28915 if (! zIndex) { 28916 zIndex = Dom.getStyle(el, "zIndex"); 28917 if (! zIndex || isNaN(zIndex)) { 28918 zIndex = 0; 28919 } 28920 } 28921 28922 if (this.iframe || this.cfg.getProperty("iframe") === true) { 28923 if (zIndex <= 0) { 28924 zIndex = 1; 28925 } 28926 } 28927 28928 Dom.setStyle(el, "zIndex", zIndex); 28929 this.cfg.setProperty("zIndex", zIndex, true); 28930 28931 if (this.iframe) { 28932 this.stackIframe(); 28933 } 28934 }, 28935 28936 /** 28937 * The default event handler fired when the "xy" property is changed. 28938 * @method configXY 28939 * @param {String} type The CustomEvent type (usually the property name) 28940 * @param {Object[]} args The CustomEvent arguments. For configuration 28941 * handlers, args[0] will equal the newly applied value for the property. 28942 * @param {Object} obj The scope object. For configuration handlers, 28943 * this will usually equal the owner. 28944 */ 28945 configXY: function (type, args, obj) { 28946 28947 var pos = args[0], 28948 x = pos[0], 28949 y = pos[1]; 28950 28951 this.cfg.setProperty("x", x); 28952 this.cfg.setProperty("y", y); 28953 28954 this.beforeMoveEvent.fire([x, y]); 28955 28956 x = this.cfg.getProperty("x"); 28957 y = this.cfg.getProperty("y"); 28958 28959 YAHOO.log(("xy: " + [x, y]), "iframe"); 28960 28961 this.cfg.refireEvent("iframe"); 28962 this.moveEvent.fire([x, y]); 28963 }, 28964 28965 /** 28966 * The default event handler fired when the "x" property is changed. 28967 * @method configX 28968 * @param {String} type The CustomEvent type (usually the property name) 28969 * @param {Object[]} args The CustomEvent arguments. For configuration 28970 * handlers, args[0] will equal the newly applied value for the property. 28971 * @param {Object} obj The scope object. For configuration handlers, 28972 * this will usually equal the owner. 28973 */ 28974 configX: function (type, args, obj) { 28975 28976 var x = args[0], 28977 y = this.cfg.getProperty("y"); 28978 28979 this.cfg.setProperty("x", x, true); 28980 this.cfg.setProperty("y", y, true); 28981 28982 this.beforeMoveEvent.fire([x, y]); 28983 28984 x = this.cfg.getProperty("x"); 28985 y = this.cfg.getProperty("y"); 28986 28987 Dom.setX(this.element, x, true); 28988 28989 this.cfg.setProperty("xy", [x, y], true); 28990 28991 this.cfg.refireEvent("iframe"); 28992 this.moveEvent.fire([x, y]); 28993 }, 28994 28995 /** 28996 * The default event handler fired when the "y" property is changed. 28997 * @method configY 28998 * @param {String} type The CustomEvent type (usually the property name) 28999 * @param {Object[]} args The CustomEvent arguments. For configuration 29000 * handlers, args[0] will equal the newly applied value for the property. 29001 * @param {Object} obj The scope object. For configuration handlers, 29002 * this will usually equal the owner. 29003 */ 29004 configY: function (type, args, obj) { 29005 29006 var x = this.cfg.getProperty("x"), 29007 y = args[0]; 29008 29009 this.cfg.setProperty("x", x, true); 29010 this.cfg.setProperty("y", y, true); 29011 29012 this.beforeMoveEvent.fire([x, y]); 29013 29014 x = this.cfg.getProperty("x"); 29015 y = this.cfg.getProperty("y"); 29016 29017 Dom.setY(this.element, y, true); 29018 29019 this.cfg.setProperty("xy", [x, y], true); 29020 29021 this.cfg.refireEvent("iframe"); 29022 this.moveEvent.fire([x, y]); 29023 }, 29024 29025 /** 29026 * Shows the iframe shim, if it has been enabled. 29027 * @method showIframe 29028 */ 29029 showIframe: function () { 29030 29031 var oIFrame = this.iframe, 29032 oParentNode; 29033 29034 if (oIFrame) { 29035 oParentNode = this.element.parentNode; 29036 29037 if (oParentNode != oIFrame.parentNode) { 29038 this._addToParent(oParentNode, oIFrame); 29039 } 29040 oIFrame.style.display = "block"; 29041 } 29042 }, 29043 29044 /** 29045 * Hides the iframe shim, if it has been enabled. 29046 * @method hideIframe 29047 */ 29048 hideIframe: function () { 29049 if (this.iframe) { 29050 this.iframe.style.display = "none"; 29051 } 29052 }, 29053 29054 /** 29055 * Syncronizes the size and position of iframe shim to that of its 29056 * corresponding Overlay instance. 29057 * @method syncIframe 29058 */ 29059 syncIframe: function () { 29060 29061 var oIFrame = this.iframe, 29062 oElement = this.element, 29063 nOffset = Overlay.IFRAME_OFFSET, 29064 nDimensionOffset = (nOffset * 2), 29065 aXY; 29066 29067 if (oIFrame) { 29068 // Size <iframe> 29069 oIFrame.style.width = (oElement.offsetWidth + nDimensionOffset + "px"); 29070 oIFrame.style.height = (oElement.offsetHeight + nDimensionOffset + "px"); 29071 29072 // Position <iframe> 29073 aXY = this.cfg.getProperty("xy"); 29074 29075 if (!Lang.isArray(aXY) || (isNaN(aXY[0]) || isNaN(aXY[1]))) { 29076 this.syncPosition(); 29077 aXY = this.cfg.getProperty("xy"); 29078 } 29079 Dom.setXY(oIFrame, [(aXY[0] - nOffset), (aXY[1] - nOffset)]); 29080 } 29081 }, 29082 29083 /** 29084 * Sets the zindex of the iframe shim, if it exists, based on the zindex of 29085 * the Overlay element. The zindex of the iframe is set to be one less 29086 * than the Overlay element's zindex. 29087 * 29088 * <p>NOTE: This method will not bump up the zindex of the Overlay element 29089 * to ensure that the iframe shim has a non-negative zindex. 29090 * If you require the iframe zindex to be 0 or higher, the zindex of 29091 * the Overlay element should be set to a value greater than 0, before 29092 * this method is called. 29093 * </p> 29094 * @method stackIframe 29095 */ 29096 stackIframe: function () { 29097 if (this.iframe) { 29098 var overlayZ = Dom.getStyle(this.element, "zIndex"); 29099 if (!YAHOO.lang.isUndefined(overlayZ) && !isNaN(overlayZ)) { 29100 Dom.setStyle(this.iframe, "zIndex", (overlayZ - 1)); 29101 } 29102 } 29103 }, 29104 29105 /** 29106 * The default event handler fired when the "iframe" property is changed. 29107 * @method configIframe 29108 * @param {String} type The CustomEvent type (usually the property name) 29109 * @param {Object[]} args The CustomEvent arguments. For configuration 29110 * handlers, args[0] will equal the newly applied value for the property. 29111 * @param {Object} obj The scope object. For configuration handlers, 29112 * this will usually equal the owner. 29113 */ 29114 configIframe: function (type, args, obj) { 29115 29116 var bIFrame = args[0]; 29117
29118 function createIFrame() { 29119 29120 var oIFrame = this.iframe, 29121 oElement = this.element, 29122 oParent; 29123 29124 if (!oIFrame) { 29125 if (!m_oIFrameTemplate) { 29126 m_oIFrameTemplate = document.createElement("iframe"); 29127 29128 if (this.isSecure) { 29129 m_oIFrameTemplate.src = Overlay.IFRAME_SRC; 29130 } 29131 29132 /* 29133 Set the opacity of the <iframe> to 0 so that it 29134 doesn't modify the opacity of any transparent 29135 elements that may be on top of it (like a shadow). 29136 */ 29137 if (UA.ie) { 29138 m_oIFrameTemplate.style.filter = "alpha(opacity=0)"; 29139 /* 29140 Need to set the "frameBorder" property to 0 29141 supress the default <iframe> border in IE. 29142 Setting the CSS "border" property alone 29143 doesn't supress it. 29144 */ 29145 m_oIFrameTemplate.frameBorder = 0; 29146 } 29147 else { 29148 m_oIFrameTemplate.style.opacity = "0"; 29149 } 29150 29151 m_oIFrameTemplate.style.position = "absolute"; 29152 m_oIFrameTemplate.style.border = "none"; 29153 m_oIFrameTemplate.style.margin = "0"; 29154 m_oIFrameTemplate.style.padding = "0"; 29155 m_oIFrameTemplate.style.display = "none"; 29156 m_oIFrameTemplate.tabIndex = -1; 29157 m_oIFrameTemplate.className = Overlay.CSS_IFRAME; 29158 } 29159 29160 oIFrame = m_oIFrameTemplate.cloneNode(false); 29161 oIFrame.id = this.id + "_f"; 29162 oParent = oElement.parentNode; 29163 29164 var parentNode = oParent || document.body; 29165 29166 this._addToParent(parentNode, oIFrame); 29167 this.iframe = oIFrame; 29168 } 29169 29170 /* 29171 Show the <iframe> before positioning it since the "setXY" 29172 method of DOM requires the element be in the document 29173 and visible. 29174 */ 29175 this.showIframe(); 29176 29177 /* 29178 Syncronize the size and position of the <iframe> to that 29179 of the Overlay. 29180 */ 29181 this.syncIframe(); 29182 this.stackIframe(); 29183 29184 // Add event listeners to update the <iframe> when necessary 29185 if (!this._hasIframeEventListeners) { 29186 this.showEvent.subscribe(this.showIframe); 29187 this.hideEvent.subscribe(this.hideIframe); 29188 this.changeContentEvent.subscribe(this.syncIframe); 29189 29190 this._hasIframeEventListeners = true; 29191 } 29192 } 29193 29194 function onBeforeShow() { 29195 createIFrame.call(this); 29196 this.beforeShowEvent.unsubscribe(onBeforeShow); 29197 this._iframeDeferred = false; 29198 } 29199 29200 if (bIFrame) { // <iframe> shim is enabled 29201 29202 if (this.cfg.getProperty("visible")) { 29203 createIFrame.call(this); 29204 } else { 29205 if (!this._iframeDeferred) { 29206 this.beforeShowEvent.subscribe(onBeforeShow); 29207 this._iframeDeferred = true; 29208 } 29209 } 29210 29211 } else { // <iframe> shim is disabled 29212 this.hideIframe(); 29213 29214 if (this._hasIframeEventListeners) { 29215 this.showEvent.unsubscribe(this.showIframe); 29216 this.hideEvent.unsubscribe(this.hideIframe); 29217 this.changeContentEvent.unsubscribe(this.syncIframe); 29218 29219 this._hasIframeEventListeners = false;
29220 } 29221 } 29222 }, 29223 29224 /** 29225 * Set's the container's XY value from DOM if not already set. 29226 * 29227 * Differs from syncPosition, in that the XY value is only sync'd with DOM if 29228 * not already set. The method also refire's the XY config property event, so any 29229 * beforeMove, Move event listeners are invoked. 29230 * 29231 * @method _primeXYFromDOM 29232 * @protected 29233 */ 29234 _primeXYFromDOM : function() { 29235 if (YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))) { 29236 // Set CFG XY based on DOM XY 29237 this.syncPosition(); 29238 // Account for XY being set silently in syncPosition (no moveTo fired/called) 29239 this.cfg.refireEvent("xy"); 29240 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); 29241 } 29242 }, 29243 29244 /** 29245 * The default event handler fired when the "constraintoviewport" 29246 * property is changed. 29247 * @method configConstrainToViewport 29248 * @param {String} type The CustomEvent type (usually the property name) 29249 * @param {Object[]} args The CustomEvent arguments. For configuration 29250 * handlers, args[0] will equal the newly applied value for 29251 * the property. 29252 * @param {Object} obj The scope object. For configuration handlers, 29253 * this will usually equal the owner. 29254 */ 29255 configConstrainToViewport: function (type, args, obj) { 29256 var val = args[0]; 29257 29258 if (val) { 29259 if (! Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) { 29260 this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true); 29261 } 29262 if (! Config.alreadySubscribed(this.beforeShowEvent, this._primeXYFromDOM)) { 29263 this.beforeShowEvent.subscribe(this._primeXYFromDOM); 29264 } 29265 } else { 29266 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); 29267 this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this); 29268 } 29269 }, 29270 29271 /** 29272 * The default event handler fired when the "context" property 29273 * is changed. 29274 * 29275 * @method configContext 29276 * @param {String} type The CustomEvent type (usually the property name) 29277 * @param {Object[]} args The CustomEvent arguments. For configuration 29278 * handlers, args[0] will equal the newly applied value for the property. 29279 * @param {Object} obj The scope object. For configuration handlers, 29280 * this will usually equal the owner. 29281 */ 29282 configContext: function (type, args, obj) { 29283 29284 var contextArgs = args[0], 29285 contextEl, 29286 elementMagnetCorner, 29287 contextMagnetCorner, 29288 triggers, 29289 offset, 29290 defTriggers = this.CONTEXT_TRIGGERS; 29291 29292 if (contextArgs) { 29293 29294 contextEl = contextArgs[0]; 29295 elementMagnetCorner = contextArgs[1]; 29296 contextMagnetCorner = contextArgs[2]; 29297 triggers = contextArgs[3]; 29298 offset = contextArgs[4]; 29299 29300 if (defTriggers && defTriggers.length > 0) { 29301 triggers = (triggers || []).concat(defTriggers); 29302 } 29303 29304 if (contextEl) { 29305 if (typeof contextEl == "string") { 29306 this.cfg.setProperty("context", [ 29307 document.getElementById(contextEl), 29308 elementMagnetCorner, 29309 contextMagnetCorner, 29310 triggers, 29311 offset], 29312 true); 29313 } 29314 29315 if (elementMagnetCorner && contextMagnetCorner) { 29316 this.align(elementMagnetCorner, contextMagnetCorner, offset); 29317 } 29318 29319 if (this._contextTriggers) { 29320 // Unsubscribe Old Set 29321 this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger); 29322 } 29323 29324 if (triggers) { 29325 // Subscribe New Set 29326 this._processTriggers(triggers, _SUBSCRIBE, this._alignOnTrigger); 29327 this._contextTriggers = triggers; 29328 } 29329 } 29330 } 29331 }, 29332 29333 /** 29334 * Custom Event handler for context alignment triggers. Invokes the align method 29335 * 29336 * @method _alignOnTrigger 29337 * @protected 29338 * 29339 * @param {String} type The event type (not used by the default implementation) 29340 * @param {Any[]} args The array of arguments for the trigger event (not used by the default implementation) 29341 */ 29342 _alignOnTrigger: function(type, args) { 29343 this.align(); 29344 }, 29345 29346 /** 29347 * Helper method to locate the custom event instance for the event name string 29348 * passed in. As a convenience measure, any custom events passed in are returned. 29349 * 29350 * @method _findTriggerCE 29351 * @private 29352 * 29353 * @param {String|CustomEvent} t Either a CustomEvent, or event type (e.g. "windowScroll") for which a 29354 * custom event instance needs to be looked up from the Overlay._TRIGGER_MAP. 29355 */ 29356 _findTriggerCE : function(t) { 29357 var tce = null; 29358 if (t instanceof CustomEvent) { 29359 tce = t; 29360 } else if (Overlay._TRIGGER_MAP[t]) { 29361 tce = Overlay._TRIGGER_MAP[t]; 29362 } 29363 return tce; 29364 }, 29365 29366 /** 29367 * Utility method that subscribes or unsubscribes the given 29368 * function from the list of trigger events provided. 29369 * 29370 * @method _processTriggers 29371 * @protected 29372 * 29373 * @param {Array[String|CustomEvent]} triggers An array of either CustomEvents, event type strings 29374 * (e.g. "beforeShow", "windowScroll") to/from which the provided function should be 29375 * subscribed/unsubscribed respectively. 29376 * 29377 * @param {String} mode Either "subscribe" or "unsubscribe", specifying whether or not 29378 * we are subscribing or unsubscribing trigger listeners 29379 * 29380 * @param {Function} fn The function to be subscribed/unsubscribed to/from the trigger event.
29381 * Context is always set to the overlay instance, and no additional object argument 29382 * get passed to the subscribed function. 29383 */ 29384 _processTriggers : function(triggers, mode, fn) { 29385 var t, tce; 29386 29387 for (var i = 0, l = triggers.length; i < l; ++i) { 29388 t = triggers[i]; 29389 tce = this._findTriggerCE(t); 29390 if (tce) { 29391 tce[mode](fn, this, true); 29392 } else { 29393 this[mode](t, fn); 29394 } 29395 } 29396 }, 29397 29398 // END BUILT-IN PROPERTY EVENT HANDLERS // 29399 /** 29400 * Aligns the Overlay to its context element using the specified corner 29401 * points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, 29402 * and BOTTOM_RIGHT. 29403 * @method align 29404 * @param {String} elementAlign The String representing the corner of 29405 * the Overlay that should be aligned to the context element 29406 * @param {String} contextAlign The corner of the context element 29407 * that the elementAlign corner should stick to. 29408 * @param {Number[]} xyOffset Optional. A 2 element array specifying the x and y pixel offsets which should be applied 29409 * after aligning the element and context corners. For example, passing in [5, -10] for this value, would offset the 29410 * Overlay by 5 pixels along the X axis (horizontally) and -10 pixels along the Y axis (vertically) after aligning the specified corners. 29411 */ 29412 align: function (elementAlign, contextAlign, xyOffset) { 29413 29414 var contextArgs = this.cfg.getProperty("context"), 29415 me = this, 29416 context, 29417 element, 29418 contextRegion; 29419 29420 function doAlign(v, h) { 29421 29422 var alignX = null, alignY = null; 29423 29424 switch (elementAlign) { 29425 29426 case Overlay.TOP_LEFT: 29427 alignX = h; 29428 alignY = v; 29429 break; 29430 29431 case Overlay.TOP_RIGHT: 29432 alignX = h - element.offsetWidth; 29433 alignY = v; 29434 break; 29435 29436 case Overlay.BOTTOM_LEFT: 29437 alignX = h; 29438 alignY = v - element.offsetHeight; 29439 break; 29440 29441 case Overlay.BOTTOM_RIGHT: 29442 alignX = h - element.offsetWidth; 29443 alignY = v - element.offsetHeight; 29444 break; 29445 } 29446 29447 if (alignX !== null && alignY !== null) { 29448 if (xyOffset) { 29449 alignX += xyOffset[0]; 29450 alignY += xyOffset[1]; 29451 } 29452 me.moveTo(alignX, alignY); 29453 } 29454 } 29455 29456 if (contextArgs) { 29457 context = contextArgs[0]; 29458 element = this.element; 29459 me = this; 29460 29461 if (! elementAlign) { 29462 elementAlign = contextArgs[1]; 29463 } 29464 29465 if (! contextAlign) { 29466 contextAlign = contextArgs[2]; 29467 } 29468 29469 if (!xyOffset && contextArgs[4]) { 29470 xyOffset = contextArgs[4]; 29471 } 29472 29473 if (element && context) { 29474 contextRegion = Dom.getRegion(context); 29475 29476 switch (contextAlign) { 29477 29478 case Overlay.TOP_LEFT: 29479 doAlign(contextRegion.top, contextRegion.left); 29480 break; 29481 29482 case Overlay.TOP_RIGHT: 29483 doAlign(contextRegion.top, contextRegion.right); 29484 break; 29485 29486 case Overlay.BOTTOM_LEFT: 29487 doAlign(contextRegion.bottom, contextRegion.left); 29488 break; 29489 29490 case Overlay.BOTTOM_RIGHT: 29491 doAlign(contextRegion.bottom, contextRegion.right); 29492 break; 29493 } 29494 } 29495 } 29496 }, 29497 29498 /** 29499 * The default event handler executed when the moveEvent is fired, if the 29500 * "constraintoviewport" is set to true. 29501 * @method enforceConstraints 29502 * @param {String} type The CustomEvent type (usually the property name) 29503 * @param {Object[]} args The CustomEvent arguments. For configuration 29504 * handlers, args[0] will equal the newly applied value for the property. 29505 * @param {Object} obj The scope object. For configuration handlers, 29506 * this will usually equal the owner. 29507 */ 29508 enforceConstraints: function (type, args, obj) { 29509 var pos = args[0]; 29510 29511 var cXY = this.getConstrainedXY(pos[0], pos[1]); 29512 this.cfg.setProperty("x", cXY[0], true); 29513 this.cfg.setProperty("y", cXY[1], true); 29514 this.cfg.setProperty("xy", cXY, true); 29515 }, 29516 29517 /** 29518 * Shared implementation method for getConstrainedX and getConstrainedY. 29519 * 29520 * <p> 29521 * Given a coordinate value, returns the calculated coordinate required to 29522 * position the Overlay if it is to be constrained to the viewport, based on the 29523 * current element size, viewport dimensions, scroll values and preventoverlap 29524 * settings 29525 * </p> 29526 * 29527 * @method _getConstrainedPos 29528 * @protected 29529 * @param {String} pos The coordinate which needs to be constrained, either "x" or "y" 29530 * @param {Number} The coordinate value which needs to be constrained 29531 * @return {Number} The constrained coordinate value 29532 */ 29533 _getConstrainedPos: function(pos, val) { 29534 29535 var overlayEl = this.element, 29536 29537 buffer = Overlay.VIEWPORT_OFFSET, 29538 29539 x = (pos == "x"), 29540 29541 overlaySize = (x) ? overlayEl.offsetWidth : overlayEl.offsetHeight, 29542 viewportSize = (x) ? Dom.getViewportWidth() : Dom.getViewportHe
29542ight(), 29543 docScroll = (x) ? Dom.getDocumentScrollLeft() : Dom.getDocumentScrollTop(), 29544 overlapPositions = (x) ? Overlay.PREVENT_OVERLAP_X : Overlay.PREVENT_OVERLAP_Y, 29545 29546 context = this.cfg.getProperty("context"), 29547 29548 bOverlayFitsInViewport = (overlaySize + buffer < viewportSize), 29549 bPreventContextOverlap = this.cfg.getProperty("preventcontextoverlap") && context && overlapPositions[(context[1] + context[2])], 29550 29551 minConstraint = docScroll + buffer, 29552 maxConstraint = docScroll + viewportSize - overlaySize - buffer, 29553 29554 constrainedVal = val; 29555 29556 if (val < minConstraint || val > maxConstraint) { 29557 if (bPreventContextOverlap) { 29558 constrainedVal = this._preventOverlap(pos, context[0], overlaySize, viewportSize, docScroll); 29559 } else { 29560 if (bOverlayFitsInViewport) { 29561 if (val < minConstraint) { 29562 constrainedVal = minConstraint; 29563 } else if (val > maxConstraint) { 29564 constrainedVal = maxConstraint; 29565 } 29566 } else { 29567 constrainedVal = minConstraint; 29568 } 29569 } 29570 } 29571 29572 return constrainedVal; 29573 }, 29574 29575 /** 29576 * Helper method, used to position the Overlap to prevent overlap with the 29577 * context element (used when preventcontextoverlap is enabled) 29578 * 29579 * @method _preventOverlap 29580 * @protected 29581 * @param {String} pos The coordinate to prevent overlap for, either "x" or "y". 29582 * @param {HTMLElement} contextEl The context element 29583 * @param {Number} overlaySize The related overlay dimension value (for "x", the width, for "y", the height) 29584 * @param {Number} viewportSize The related viewport dimension value (for "x", the width, for "y", the height) 29585 * @param {Object} docScroll The related document scroll value (for "x", the scrollLeft, for "y", the scrollTop) 29586 * 29587 * @return {Number} The new coordinate value which was set to prevent overlap 29588 */ 29589 _preventOverlap : function(pos, contextEl, overlaySize, viewportSize, docScroll) { 29590 29591 var x = (pos == "x"), 29592 29593 buffer = Overlay.VIEWPORT_OFFSET, 29594 29595 overlay = this, 29596 29597 contextElPos = ((x) ? Dom.getX(contextEl) : Dom.getY(contextEl)) - docScroll, 29598 contextElSize = (x) ? contextEl.offsetWidth : contextEl.offsetHeight, 29599 29600 minRegionSize = contextElPos - buffer, 29601 maxRegionSize = (viewportSize - (contextElPos + contextElSize)) - buffer, 29602 29603 bFlipped = false, 29604 29605 flip = function () { 29606 var flippedVal; 29607 29608 if ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) { 29609 flippedVal = (contextElPos - overlaySize); 29610 } else { 29611 flippedVal = (contextElPos + contextElSize); 29612 } 29613 29614 overlay.cfg.setProperty(pos, (flippedVal + docScroll), true); 29615 29616 return flippedVal; 29617 }, 29618 29619 setPosition = function () { 29620 29621 var displayRegionSize = ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) ? maxRegionSize : minRegionSize, 29622 position; 29623 29624 if (overlaySize > displayRegionSize) { 29625 if (bFlipped) { 29626 /* 29627 All possible positions and values have been 29628 tried, but none were successful, so fall back 29629 to the original size and position. 29630 */ 29631 flip(); 29632 } else { 29633 flip(); 29634 bFlipped = true; 29635 position = setPosition(); 29636 } 29637 } 29638 29639 return position; 29640 }; 29641 29642 setPosition(); 29643 29644 return this.cfg.getProperty(pos); 29645 }, 29646 29647 /** 29648 * Given x coordinate value, returns the calculated x coordinate required to 29649 * position the Overlay if it is to be constrained to the viewport, based on the 29650 * current element size, viewport dimensions and scroll values. 29651 * 29652 * @param {Number} x The X coordinate value to be constrained 29653 * @return {Number} The constrained x coordinate 29654 */ 29655 getConstrainedX: function (x) { 29656 return this._getConstrainedPos("x", x); 29657 }, 29658 29659 /** 29660 * Given y coordinate value, returns the calculated y co
29660ordinate required to 29661 * position the Overlay if it is to be constrained to the viewport, based on the 29662 * current element size, viewport dimensions and scroll values. 29663 * 29664 * @param {Number} y The Y coordinate value to be constrained 29665 * @return {Number} The constrained y coordinate 29666 */ 29667 getConstrainedY : function (y) { 29668 return this._getConstrainedPos("y", y); 29669 }, 29670 29671 /** 29672 * Given x, y coordinate values, returns the calculated coordinates required to 29673 * position the Overlay if it is to be constrained to the viewport, based on the 29674 * current element size, viewport dimensions and scroll values. 29675 * 29676 * @param {Number} x The X coordinate value to be constrained 29677 * @param {Number} y The Y coordinate value to be constrained 29678 * @return {Array} The constrained x and y coordinates at index 0 and 1 respectively; 29679 */ 29680 getConstrainedXY: function(x, y) { 29681 return [this.getConstrainedX(x), this.getConstrainedY(y)]; 29682 }, 29683 29684 /** 29685 * Centers the container in the viewport. 29686 * @method center 29687 */ 29688 center: function () { 29689 29690 var nViewportOffset = Overlay.VIEWPORT_OFFSET, 29691 elementWidth = this.element.offsetWidth, 29692 elementHeight = this.element.offsetHeight, 29693 viewPortWidth = Dom.getViewportWidth(), 29694 viewPortHeight = Dom.getViewportHeight(), 29695 x, 29696 y; 29697 29698 if (elementWidth < viewPortWidth) { 29699 x = (viewPortWidth / 2) - (elementWidth / 2) + Dom.getDocumentScrollLeft(); 29700 } else { 29701 x = nViewportOffset + Dom.getDocumentScrollLeft(); 29702 } 29703 29704 if (elementHeight < viewPortHeight) { 29705 y = (viewPortHeight / 2) - (elementHeight / 2) + Dom.getDocumentScrollTop(); 29706 } else { 29707 y = nViewportOffset + Dom.getDocumentScrollTop(); 29708 } 29709 29710 this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]); 29711 this.cfg.refireEvent("iframe"); 29712 29713 if (UA.webkit) { 29714 this.forceContainerRedraw(); 29715 } 29716 }, 29717 29718 /** 29719 * Synchronizes the Panel's "xy", "x", and "y" properties with the 29720 * Panel's position in the DOM. This is primarily used to update 29721 * position information during drag & drop. 29722 * @method syncPosition 29723 */ 29724 syncPosition: function () { 29725 29726 var pos = Dom.getXY(this.element); 29727 29728 this.cfg.setProperty("x", pos[0], true); 29729 this.cfg.setProperty("y", pos[1], true); 29730 this.cfg.setProperty("xy", pos, true); 29731 29732 }, 29733 29734 /** 29735 * Event handler fired when the resize monitor element is resized. 29736 * @method onDomResize 29737 * @param {DOMEvent} e The resize DOM event 29738 * @param {Object} obj The scope object 29739 */ 29740 onDomResize: function (e, obj) { 29741 29742 var me = this; 29743 29744 Overlay.superclass.onDomResize.call(this, e, obj); 29745 29746 setTimeout(function () { 29747 me.syncPosition(); 29748 me.cfg.refireEvent("iframe"); 29749 me.cfg.refireEvent("context"); 29750 }, 0); 29751 }, 29752 29753 /** 29754 * Determines the content box height of the given element (height of the element, without padding or borders) in pixels. 29755 * 29756 * @method _getComputedHeight 29757 * @private 29758 * @param {HTMLElement} el The element for which the content height needs to be determined 29759 * @return {Number} The content box height of the given element, or null if it could not be determined. 29760 */ 29761 _getComputedHeight : (function() { 29762 29763 if (document.defaultView && document.defaultView.getComputedStyle) { 29764 return function(el) { 29765 var height = null; 29766 if (el.ownerDocument && el.ownerDocument.defaultView) { 29767 var computed = el.ownerDocument.defaultView.getComputedStyle(el, ''); 29768 if (computed) { 29769 height = parseInt(computed.height, 10); 29770 } 29771 } 29772 return (Lang.isNumber(height)) ? height : null; 29773 }; 29774 } else { 29775 return function(el) { 29776 var height = null; 29777 if (el.style.pixelHeight) { 29778 height = el.style.pixelHeight; 29779 } 29780 return (Lang.isNumber(height)) ? height : null; 29781 }; 29782 } 29783 })(), 29784 29785 /** 29786 * autofillheight validator. Verifies that the autofill value is either null 29787 * or one of the strings : "body", "header" or "footer". 29788 * 29789 * @method _validateAutoFillHeight 29790 * @protected 29791 * @param {String} val
29792 * @return true, if valid, false otherwise 29793 */ 29794 _validateAutoFillHeight : function(val) { 29795 return (!val) || (Lang.isString(val) && Overlay.STD_MOD_RE.test(val)); 29796 }, 29797 29798 /** 29799 * The default custom event handler executed when the overlay's height is changed, 29800 * if the autofillheight property has been set. 29801 * 29802 * @method _autoFillOnHeightChange 29803 * @protected 29804 * @param {String} type The event type 29805 * @param {Array} args The array of arguments passed to event subscribers 29806 * @param {HTMLElement} el The header, body or footer element which is to be resized to fill 29807 * out the containers height 29808 */ 29809 _autoFillOnHeightChange : function(type, args, el) { 29810 var height = this.cfg.getProperty("height"); 29811 if ((height && height !== "auto") || (height === 0)) { 29812 this.fillHeight(el); 29813 } 29814 }, 29815 29816 /** 29817 * Returns the sub-pixel height of the el, using getBoundingClientRect, if available, 29818 * otherwise returns the offsetHeight 29819 * @method _getPreciseHeight 29820 * @private 29821 * @param {HTMLElement} el 29822 * @return {Float} The sub-pixel height if supported by the browser, else the rounded height. 29823 */ 29824 _getPreciseHeight : function(el) { 29825 var height = el.offsetHeight; 29826 29827 if (el.getBoundingClientRect) { 29828 var rect = el.getBoundingClientRect(); 29829 height = rect.bottom - rect.top; 29830 } 29831 29832 return height; 29833 }, 29834 29835 /** 29836 * <p> 29837 * Sets the height on the provided header, body or footer element to 29838 * fill out the height of the container. It determines the height of the 29839 * containers content box, based on it's configured height value, and 29840 * sets the height of the autofillheight element to fill out any 29841 * space remaining after the other standard module element heights 29842 * have been accounted for. 29843 * </p> 29844 * <p><strong>NOTE:</strong> This method is not designed to work if an explicit 29845 * height has not been set on the container, since for an "auto" height container, 29846 * the heights of the header/body/footer will drive the height of the container.</p> 29847 * 29848 * @method fillHeight 29849 * @param {HTMLElement} el The element which should be resized to fill out the height 29850 * of the container element. 29851 */ 29852 fillHeight : function(el) { 29853 if (el) { 29854 var container = this.innerElement || this.element, 29855 containerEls = [this.header, this.body, this.footer], 29856 containerEl, 29857 total = 0, 29858 filled = 0, 29859 remaining = 0, 29860 validEl = false; 29861 29862 for (var i = 0, l = containerEls.length; i < l; i++) { 29863 containerEl = containerEls[i]; 29864 if (containerEl) { 29865 if (el !== containerEl) { 29866 filled += this._getPreciseHeight(containerEl); 29867 } else { 29868 validEl = true; 29869 } 29870 } 29871 } 29872 29873 if (validEl) { 29874 29875 if (UA.ie || UA.opera) { 29876 // Need to set height to 0, to allow height to be reduced 29877 Dom.setStyle(el, 'height', 0 + 'px'); 29878 } 29879 29880 total = this._getComputedHeight(container); 29881 29882 // Fallback, if we can't get computed value for content height 29883 if (total === null) { 29884 Dom.addClass(container, "yui-override-padding"); 29885 total = container.clientHeight; // Content, No Border, 0 Padding (set by yui-override-padding) 29886 Dom.removeClass(container, "yui-override-padding"); 29887 } 29888 29889 remaining = Math.max(total - filled, 0); 29890 29891 Dom.setStyle(el, "height", remaining + "px"); 29892 29893 // Re-adjust height if required, to account for el padding and border 29894 if (el.offsetHeight != remaining) { 29895 remaining = Math.max(remaining - (el.offsetHeight - remaining), 0); 29896 } 29897 Dom.setStyle(el, "height", remaining + "px"); 29898 } 29899 } 29900 }, 29901 29902 /** 29903 * Places the Overlay on top of all other instances of 29904 * YAHOO.widget.Overlay. 29905 * @method bringToTop 29906 */ 29907 bringToTop: function () { 29908 29909 var aOverlays = [], 29910 oElement = this.element; 29911 29912 function compareZIndexDesc(p_oOverlay1, p_oOverlay2) { 29913 29914 var sZIndex1 = Dom.getStyle(p_oOverlay1, "zIndex"), 29915 sZIndex2 = Dom.getStyle(p_oOverlay2, "zIndex"), 29916 29917 nZIndex1 = (!sZIndex1 || isNaN(sZIndex1)) ? 0 : parseInt(sZIndex1, 10), 29918 nZIndex2 = (!sZIndex2 || isNaN(sZIndex2)) ? 0 : parseInt(sZIndex2, 10); 29919 29920 if (nZIndex1 > nZIndex2) { 29921 return -1; 29922 } else if (nZIndex1 < nZIndex2) { 29923 return 1; 29924 } else { 29925 return 0; 29926 } 29927 } 29928 29929 function isOverlayElement(p_oElement) { 29930 29931 var isOverlay = Dom.hasClass(p_oElement, Overlay.CSS_OVERLAY), 29932 Panel = YAHOO.widget.Panel; 29933 29934 if (isOverlay && !Dom.isAncestor(oElement, p_oElement)) { 29935 if (Panel && Dom.hasClass(p_oElement, Panel.CSS_PANEL)) { 29936 aOverlays[aOverlays.length] = p_oElement.parentNode; 29937 } else { 29938 aOverlays[aOverlays.length] = p_oElement; 29939 } 29940 } 29941 } 29942 29943 Dom.getElementsBy(isOverlayElement, "div", document.body); 29944 29945 aOverlays.sort(compareZIndexDesc); 29946 29947 var oTopOverlay = aOverlays[0], 29948 nTopZIndex; 29949 29950 if (oTopOverlay) { 29951 nTopZIndex = Dom.getStyle(oTopOverlay, "zIndex"); 29952 29953 if (!isNaN(nTopZIndex)) { 29954 var bRequiresBump = false;
29955 29956 if (oTopOverlay != oElement) { 29957 bRequiresBump = true; 29958 } else if (aOverlays.length > 1) { 29959 var nNextZIndex = Dom.getStyle(aOverlays[1], "zIndex"); 29960 // Don't rely on DOM order to stack if 2 overlays are at the same zindex. 29961 if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { 29962 bRequiresBump = true; 29963 } 29964 } 29965 if (bRequiresBump) { 29966 this.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); 29967 } 29968 } 29969 } 29970 }, 29971 29972 /** 29973 * Removes the Overlay element from the DOM and sets all child 29974 * elements to null. 29975 * @method destroy 29976 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 29977 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 29978 */ 29979 destroy: function (shallowPurge) { 29980 29981 if (this.iframe) { 29982 this.iframe.parentNode.removeChild(this.iframe); 29983 } 29984 29985 this.iframe = null; 29986 29987 Overlay.windowResizeEvent.unsubscribe( 29988 this.doCenterOnDOMEvent, this); 29989 29990 Overlay.windowScrollEvent.unsubscribe( 29991 this.doCenterOnDOMEvent, this); 29992 29993 Module.textResizeEvent.unsubscribe(this._autoFillOnHeightChange); 29994 29995 if (this._contextTriggers) { 29996 // Unsubscribe context triggers - to cover context triggers which listen for global 29997 // events such as windowResize and windowScroll. Easier just to unsubscribe all 29998 this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger); 29999 } 30000 30001 Overlay.superclass.destroy.call(this, shallowPurge); 30002 }, 30003 30004 /** 30005 * Can be used to force the container to repaint/redraw it's contents. 30006 * <p> 30007 * By default applies and then removes a 1px bottom margin through the 30008 * application/removal of a "yui-force-redraw" class. 30009 * </p> 30010 * <p> 30011 * It is currently used by Overlay to force a repaint for webkit 30012 * browsers, when centering. 30013 * </p> 30014 * @method forceContainerRedraw 30015 */ 30016 forceContainerRedraw : function() { 30017 var c = this; 30018 Dom.addClass(c.element, "yui-force-redraw"); 30019 setTimeout(function() { 30020 Dom.removeClass(c.element, "yui-force-redraw"); 30021 }, 0); 30022 }, 30023 30024 /** 30025 * Returns a String representation of the object. 30026 * @method toString 30027 * @return {String} The string representation of the Overlay. 30028 */ 30029 toString: function () { 30030 return "Overlay " + this.id; 30031 } 30032 30033 }); 30034}()); 30035(function () { 30036 30037 /** 30038 * OverlayManager is used for maintaining the focus status of 30039 * multiple Overlays. 30040 * @namespace YAHOO.widget 30041 * @namespace YAHOO.widget 30042 * @class OverlayManager 30043 * @constructor 30044 * @param {Array} overlays Optional. A collection of Overlays to register 30045 * with the manager. 30046 * @param {Object} userConfig The object literal representing the user 30047 * configuration of the OverlayManager 30048 */ 30049 YAHOO.widget.OverlayManager = function (userConfig) { 30050 this.init(userConfig); 30051 }; 30052 30053 var Overlay = YAHOO.widget.Overlay, 30054 Event = YAHOO.util.Event, 30055 Dom = YAHOO.util.Dom, 30056 Config = YAHOO.util.Config, 30057 CustomEvent = YAHOO.util.CustomEvent, 30058 OverlayManager = YAHOO.widget.OverlayManager; 30059 30060 /** 30061 * The CSS class representing a focused Overlay 30062 * @property OverlayManager.CSS_FOCUSED 30063 * @static 30064 * @final
30065 * @type String 30066 */ 30067 OverlayManager.CSS_FOCUSED = "focused"; 30068 30069 OverlayManager.prototype = { 30070 30071 /** 30072 * The class's constructor function 30073 * @property contructor 30074 * @type Function 30075 */ 30076 constructor: OverlayManager, 30077 30078 /** 30079 * The array of Overlays that are currently registered 30080 * @property overlays 30081 * @type YAHOO.widget.Overlay[] 30082 */ 30083 overlays: null, 30084 30085 /** 30086 * Initializes the default configuration of the OverlayManager 30087 * @method initDefaultConfig 30088 */ 30089 initDefaultConfig: function () { 30090 /** 30091 * The collection of registered Overlays in use by 30092 * the OverlayManager 30093 * @config overlays 30094 * @type YAHOO.widget.Overlay[] 30095 * @default null 30096 */ 30097 this.cfg.addProperty("overlays", { suppressEvent: true } ); 30098 30099 /** 30100 * The default DOM event that should be used to focus an Overlay 30101 * @config focusevent
30102 * @type String 30103 * @default "mousedown" 30104 */ 30105 this.cfg.addProperty("focusevent", { value: "mousedown" } ); 30106 }, 30107 30108 /** 30109 * Initializes the OverlayManager 30110 * @method init 30111 * @param {Overlay[]} overlays Optional. A collection of Overlays to 30112 * register with the manager. 30113 * @param {Object} userConfig The object literal representing the user 30114 * configuration of the OverlayManager 30115 */ 30116 init: function (userConfig) { 30117 30118 /** 30119 * The OverlayManager's Config object used for monitoring 30120 * configuration properties. 30121 * @property cfg 30122 * @type Config 30123 */ 30124 this.cfg = new Config(this); 30125 30126 this.initDefaultConfig(); 30127 30128 if (userConfig) { 30129 this.cfg.applyConfig(userConfig, true); 30130 } 30131 this.cfg.fireQueue(); 30132 30133 /** 30134 * The currently activated Overlay 30135 * @property activeOverlay 30136 * @private 30137 * @type YAHOO.widget.Overlay 30138 */ 30139 var activeOverlay = null; 30140 30141 /** 30142 * Returns the currently focused Overlay 30143 * @method getActive 30144 * @return {Overlay} The currently focused Overlay 30145 */ 30146 this.getActive = function () { 30147 return this.activeOverlay; 30148 }; 30149 30150 /** 30151 * Focuses the specified Overlay 30152 * @method focus 30153 * @param {Overlay} overlay The Overlay to focus 30154 * @param {String} overlay The id of the Overlay to focus 30155 */ 30156 this.focus = function (overlay) { 30157 var o = this.find(overlay); 30158 if (o) { 30159 o.focus(); 30160 } 30161 }; 30162 30163 /** 30164 * Removes the specified Overlay from the manager 30165 * @method remove 30166 * @param {Overlay} overlay The Overlay to remove 30167 * @param {String} overlay The id of the Overlay to remove 30168 */ 30169 this.remove = function (overlay) { 30170 30171 var o = this.find(overlay), 30172 originalZ; 30173 30174 if (o) { 30175 if (this.activeOverlay == o) { 30176 this.activeOverlay = null; 30177 } 30178 30179 var bDestroyed = (o.element === null && o.cfg === null) ? true : false; 30180 30181 if (!bDestroyed) { 30182 // Set it's zindex so that it's sorted to the end. 30183 originalZ = Dom.getStyle(o.element, "zIndex"); 30184 o.cfg.setProperty("zIndex", -1000, true); 30185 } 30186 30187 this.overlays.sort(this.compareZIndexDesc); 30188 this.overlays = this.overlays.slice(0, (this.overlays.length - 1)); 30189 30190 o.hideEvent.unsubscribe(o.blur); 30191 o.destroyEvent.unsubscribe(this._onOverlayDestroy, o); 30192 o.focusEvent.unsubscribe(this._onOverlayFocusHandler, o); 30193 o.blurEvent.unsubscribe(this._onOverlayBlurHandler, o); 30194 30195 if (!bDestroyed) { 30196 Event.removeListener(o.element, this.cfg.getProperty("focusevent"), this._onOverlayElementFocus); 30197 o.cfg.setProperty("zIndex", originalZ, true); 30198 o.cfg.setProperty("manager", null); 30199 } 30200 30201 /* _managed Flag for custom or existing. Don't want to remove existing */ 30202 if (o.focusEvent._managed) { o.focusEvent = null; } 30203 if (o.blurEvent._managed) { o.blurEvent = null; } 30204 30205 if (o.focus._managed) { o.focus = null; } 30206 if (o.blur._managed) { o.blur = null; } 30207 } 30208 }; 30209 30210 /** 30211 * Removes focus from all registered Overlays in the manager 30212 * @method blurAll 30213 */ 30214 this.blurAll = function () { 30215 30216 var nOverlays = this.overlays.length, 30217 i; 30218 30219 if (nOverlays > 0) { 30220 i = nOverlays - 1; 30221 do { 30222 this.overlays[i].blur(); 30223 } 30224 while(i--); 30225 } 30226 }; 30227 30228 /** 30229 * Updates the state of the OverlayManager and overlay, as a result of the overlay 30230 * being blurred. 30231 * 30232 * @method _manageBlur 30233 * @param {Overlay} overlay The overlay instance which got blurred. 30234 * @protected 30235 */ 30236 this._manageBlur = function (overlay) { 30237 var changed = false;
30238 if (this.activeOverlay == overlay) { 30239 Dom.removeClass(this.activeOverlay.element, OverlayManager.CSS_FOCUSED); 30240 30241 changed = true; 30242 activeOverlay = null; 30243 } 30244 return changed; 30245 }; 30246 30247 /** 30248 * Updates the state of the OverlayManager and overlay, as a result of the overlay 30249 * receiving focus. 30250 * 30251 * @method _manageFocus 30252 * @param {Overlay} overlay The overlay instance which got focus. 30253 * @protected 30254 */ 30255 this._manageFocus = function(overlay) { 30256 var changed = false; 30257 if (this.activeOverlay != overlay) { 30258 if (this.activeOverlay) { 30259 this.activeOverlay.blur(); 30260 } 30261 this.activeOverlay = overlay; 30262 this.bringToTop(this.activeOverlay); 30263 Dom.addClass(this.activeOverlay.element, OverlayManager.CSS_FOCUSED); 30264 changed = true; 30265 } 30266 return changed; 30267 }; 30268 30269 var overlays = this.cfg.getProperty("overlays"); 30270 30271 if (! this.overlays) { 30272 this.overlays = []; 30273 } 30274 30275 if (overlays) { 30276 this.register(overlays); 30277 this.overlays.sort(this.compareZIndexDesc); 30278 } 30279 }, 30280 30281 /** 30282 * @method _onOverlayElementFocus 30283 * @description Event handler for the DOM event that is used to focus 30284 * the Overlay instance as specified by the "focusevent" 30285 * configuration property. 30286 * @private 30287 * @param {Event} p_oEvent Object representing the DOM event 30288 * object passed back by the event utility (Event). 30289 */ 30290 _onOverlayElementFocus: function (p_oEvent) { 30291 30292 var oTarget = Event.getTarget(p_oEvent), 30293 oClose = this.close; 30294 30295 if (oClose && (oTarget == oClose || Dom.isAncestor(oClose, oTarget))) { 30296 this.blur(); 30297 } else { 30298 this.focus(); 30299 } 30300 }, 30301 30302 /** 30303 * @method _onOverlayDestroy 30304 * @description "destroy" event handler for the Overlay. 30305 * @private 30306 * @param {String} p_sType String representing the name of the event 30307 * that was fired. 30308 * @param {Array} p_aArgs Array of arguments sent when the event 30309 * was fired. 30310 * @param {Overlay} p_oOverlay Object representing the overlay that 30311 * fired the event. 30312 */ 30313 _onOverlayDestroy: function (p_sType, p_aArgs, p_oOverlay) { 30314 this.remove(p_oOverlay); 30315 }, 30316 30317 /** 30318 * @method _onOverlayFocusHandler 30319 * 30320 * @description focusEvent Handler, used to delegate to _manageFocus with the correct arguments. 30321 * 30322 * @private 30323 * @param {String} p_sType String representing the name of the event 30324 * that was fired. 30325 * @param {Array} p_aArgs Array of arguments sent when the event 30326 * was fired. 30327 * @param {Overlay} p_oOverlay Object representing the overlay that 30328 * fired the event. 30329 */ 30330 _onOverlayFocusHandler: function(p_sType, p_aArgs, p_oOverlay) { 30331 this._manageFocus(p_oOverlay); 30332 }, 30333 30334 /** 30335 * @method _onOverlayBlurHandler 30336 * @description blurEvent Handler, used to delegate to _manageBlur with the correct arguments. 30337 * 30338 * @private 30339 * @param {String} p_sType String representing the name of the event 30340 * that was fired. 30341 * @param {Array} p_aArgs Array of arguments sent when the event 30342 * was fired. 30343 * @param {Overlay} p_oOverlay Object representing the overlay that 30344 * fired the event. 30345 */ 30346 _onOverlayBlurHandler: function(p_sType, p_aArgs, p_oOverlay) { 30347 this._manageBlur(p_oOverlay); 30348 }, 30349 30350 /** 30351 * Subscribes to the Overlay based instance focusEvent, to allow the OverlayManager to 30352 * monitor focus state. 30353 * 30354 * If the instance already has a focusEvent (e.g. Menu), OverlayManager will subscribe 30355 * to the existing focusEvent, however if a focusEvent or focus method does not exist 30356 * on the instance, the _bindFocus method will add them, and the focus method will 30357 * update the OverlayManager's state directly. 30358 * 30359 * @method _bindFocus 30360 * @param {Overlay}
30360 overlay The overlay for which focus needs to be managed 30361 * @protected 30362 */ 30363 _bindFocus : function(overlay) { 30364 var mgr = this; 30365 30366 if (!overlay.focusEvent) { 30367 overlay.focusEvent = overlay.createEvent("focus"); 30368 overlay.focusEvent.signature = CustomEvent.LIST; 30369 overlay.focusEvent._managed = true; 30370 } else { 30371 overlay.focusEvent.subscribe(mgr._onOverlayFocusHandler, overlay, mgr); 30372 } 30373 30374 if (!overlay.focus) { 30375 Event.on(overlay.element, mgr.cfg.getProperty("focusevent"), mgr._onOverlayElementFocus, null, overlay); 30376 overlay.focus = function () { 30377 if (mgr._manageFocus(this)) { 30378 // For Panel/Dialog 30379 if (this.cfg.getProperty("visible") && this.focusFirst) { 30380 this.focusFirst(); 30381 } 30382 this.focusEvent.fire(); 30383 } 30384 }; 30385 overlay.focus._managed = true; 30386 } 30387 }, 30388 30389 /** 30390 * Subscribes to the Overlay based instance's blurEvent to allow the OverlayManager to 30391 * monitor blur state. 30392 * 30393 * If the instance already has a blurEvent (e.g. Menu), OverlayManager will subscribe 30394 * to the existing blurEvent, however if a blurEvent or blur method does not exist 30395 * on the instance, the _bindBlur method will add them, and the blur method 30396 * update the OverlayManager's state directly. 30397 * 30398 * @method _bindBlur 30399 * @param {Overlay} overlay The overlay for which blur needs to be managed 30400 * @protected 30401 */ 30402 _bindBlur : function(overlay) { 30403 var mgr = this; 30404 30405 if (!overlay.blurEvent) { 30406 overlay.blurEvent = overlay.createEvent("blur"); 30407 overlay.blurEvent.signature = CustomEvent.LIST; 30408 overlay.focusEvent._managed = true; 30409 } else { 30410 overlay.blurEvent.subscribe(mgr._onOverlayBlurHandler, overlay, mgr); 30411 } 30412 30413 if (!overlay.blur) { 30414 overlay.blur = function () { 30415 if (mgr._manageBlur(this)) { 30416 this.blurEvent.fire(); 30417 } 30418 }; 30419 overlay.blur._managed = true; 30420 } 30421 30422 overlay.hideEvent.subscribe(overlay.blur); 30423 }, 30424 30425 /** 30426 * Subscribes to the Overlay based instance's destroyEvent, to allow the Overlay 30427 * to be removed for the OverlayManager when destroyed. 30428 * 30429 * @method _bindDestroy 30430 * @param {Overlay} overlay The overlay instance being managed 30431 * @protected 30432 */ 30433 _bindDestroy : function(overlay) { 30434 var mgr = this; 30435 overlay.destroyEvent.subscribe(mgr._onOverlayDestroy, overlay, mgr); 30436 }, 30437 30438 /** 30439 * Ensures the zIndex configuration property on the managed overlay based instance 30440 * is set to the computed zIndex value from the DOM (with "auto" translating to 0). 30441 * 30442 * @method _syncZIndex 30443 * @param {Overlay} overlay The overlay instance being managed 30444 * @protected 30445 */ 30446 _syncZIndex : function(overlay) { 30447 var zIndex = Dom.getStyle(overlay.element, "zIndex"); 30448 if (!isNaN(zIndex)) { 30449 overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10)); 30450 } else { 30451 overlay.cfg.setProperty("zIndex", 0); 30452 } 30453 }, 30454 30455 /** 30456 * Registers an Overlay or an array of Overlays with the manager. Upon 30457 * registration, the Overlay receives functions for focus and blur, 30458 * along with CustomEvents for each. 30459 * 30460 * @method register 30461 * @param {Overlay} overlay An Overlay to register with the manager. 30462 * @param {Overlay[]} overlay An array of Overlays to register with 30463 * the manager. 30464 * @return {boolean} true if any Overlays are registered. 30465 */ 30466 register: function (overlay) { 30467 30468 var registered = false, 30469 i, 30470 n; 30471 30472 if (overlay instanceof Overlay) { 30473 30474 overlay.cfg.addProperty("manager", { value: this } ); 30475 30476 this._bindFocus(overlay); 30477 this._bindBlur(overlay); 30478 this._bindDestroy(overlay); 30479 this._syncZIndex(overlay); 30480 30481 this.overlays.push(overlay); 30482 this.bringToTop(overlay); 30483 30484 registered = true; 30485 30486 } else if (overlay instanceof Array) { 30487 30488 for (i = 0, n = overlay.length; i < n; i++) { 30489 registered = this.register(overlay[i]) || registered; 30490 } 30491 30492 } 30493 30494 return registered; 30495 }, 30496 30497 /** 30498 * Places the specified Overlay instance on top of all other 30499 * Overlay instances. 30500 * @method bringToTop 30501 * @param {YAHOO.widget.Overlay} p_oOverlay Object representing an 30502 * Overlay instance. 30503 * @param {String} p_oOverlay String representing the id of an 30504 * Overlay instance. 30505 */ 30506 bringToTop: function (p_oOverlay) { 30507 30508 var oOverlay = this.find(p_oOverlay), 30509 nTopZIndex, 30510 oTopOverlay, 30511 aOverlays; 30512 30513 if (oOverlay) { 30514 30515 aOverlays = this.overlays; 30516 aOverlays.sort(this.compareZIndexDesc); 30517 30518 oTopOverlay = aOverlays[0]; 30519 30520 if (oTopOverlay) { 30521 nTopZIndex = Dom.getStyle(oTopOverlay.element, "zIndex"); 30522 30523 if (!isNaN(nTopZIndex)) { 30524 30525 var bRequiresBump = false;
30526 30527 if (oTopOverlay !== oOverlay) { 30528 bRequiresBump = true; 30529 } else if (aOverlays.length > 1) { 30530 var nNextZIndex = Dom.getStyle(aOverlays[1].element, "zIndex"); 30531 // Don't rely on DOM order to stack if 2 overlays are at the same zindex. 30532 if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { 30533 bRequiresBump = true; 30534 } 30535 } 30536 30537 if (bRequiresBump) { 30538 oOverlay.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); 30539 } 30540 } 30541 aOverlays.sort(this.compareZIndexDesc); 30542 } 30543 } 30544 }, 30545 30546 /** 30547 * Attempts to locate an Overlay by instance or ID. 30548 * @method find 30549 * @param {Overlay} overlay An Overlay to locate within the manager 30550 * @param {String} overlay An Overlay id to locate within the manager 30551 * @return {Overlay} The requested Overlay, if found, or null if it 30552 * cannot be located. 30553 */ 30554 find: function (overlay) { 30555 30556 var isInstance = overlay instanceof Overlay, 30557 overlays = this.overlays, 30558 n = overlays.length, 30559 found = null, 30560 o, 30561 i; 30562 30563 if (isInstance || typeof overlay == "string") { 30564 for (i = n-1; i >= 0; i--) { 30565 o = overlays[i]; 30566 if ((isInstance && (o === overlay)) || (o.id == overlay)) { 30567 found = o; 30568 break; 30569 } 30570 } 30571 } 30572 30573 return found; 30574 }, 30575 30576 /** 30577 * Used for sorting the manager's Overlays by z-index. 30578 * @method compareZIndexDesc 30579 * @private 30580 * @return {Number} 0, 1, or -1, depending on where the Overlay should 30581 * fall in the stacking order. 30582 */ 30583 compareZIndexDesc: function (o1, o2) { 30584 30585 var zIndex1 = (o1.cfg) ? o1.cfg.getProperty("zIndex") : null, // Sort invalid (destroyed) 30586 zIndex2 = (o2.cfg) ? o2.cfg.getProperty("zIndex") : null; // objects at bottom. 30587 30588 if (zIndex1 === null && zIndex2 === null) { 30589 return 0; 30590 } else if (zIndex1 === null){ 30591 return 1; 30592 } else if (zIndex2 === null) { 30593 return -1; 30594 } else if (zIndex1 > zIndex2) { 30595 return -1; 30596 } else if (zIndex1 < zIndex2) { 30597 return 1; 30598 } else { 30599 return 0; 30600 } 30601 }, 30602 30603 /** 30604 * Shows all Overlays in the manager. 30605 * @method showAll 30606 */ 30607 showAll: function () { 30608 var overlays = this.overlays, 30609 n = overlays.length, 30610 i; 30611 30612 for (i = n - 1; i >= 0; i--) { 30613 overlays[i].show(); 30614 } 30615 }, 30616 30617 /** 30618 * Hides all Overlays in the manager. 30619 * @method hideAll 30620 */ 30621 hideAll: function () { 30622 var overlays = this.overlays, 30623 n = overlays.length, 30624 i; 30625 30626 for (i = n - 1; i >= 0; i--) { 30627 overlays[i].hide(); 30628 } 30629 }, 30630 30631 /** 30632 * Returns a string representation of the object. 30633 * @method toString 30634 * @return {String} The string representation of the OverlayManager 30635 */ 30636 toString: function () { 30637 return "OverlayManager"; 30638 } 30639 }; 30640}()); 30641(function () { 30642 30643 /** 30644 * Tooltip is an implementation of Overlay that behaves like an OS tooltip, 30645 * displaying when the user mouses over a particular element, and 30646 * disappearing on mouse out. 30647 * @namespace YAHOO.widget 30648 * @class Tooltip 30649 * @extends YAHOO.widget.Overlay 30650 * @constructor 30651 * @param {String} el The element ID representing the Tooltip <em>OR</em> 30652 * @param {HTMLElement} el The element representing the Tooltip 30653 * @param {Object} userConfig The configuration object literal containing 30654 * the configuration that should be set for this Overlay. See configuration 30655 * documentation for more details. 30656 */ 30657 YAHOO.widget.Tooltip = function (el, userConfig) { 30658 YAHOO.widget.Tooltip.superclass.constructor.call(this, el, userConfig); 30659 }; 30660 30661 var Lang = YAHOO.lang, 30662 Event = YAHOO.util.Event, 30663 CustomEvent = YAHOO.util.CustomEvent, 30664 Dom = YAHOO.util.Dom, 30665 Tooltip = YAHOO.widget.Tooltip, 30666 UA = YAHOO.env.ua, 30667 bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), 30668 30669 m_oShadowTemplate, 30670 30671 /** 30672 * Constant representing the Tooltip's configuration properties 30673 * @property DEFAULT_CONFIG 30674 * @private 30675 * @final 30676 * @type Object 30677 */ 30678 DEFAULT_CONFIG = { 30679 30680 "PREVENT_OVERLAP": { 30681 key: "preventoverlap", 30682 value: true, 30683 validator: Lang.isBoolean, 30684 supercedes: ["x", "y", "xy"] 30685 }, 30686 30687 "SHOW_DELAY": { 30688 key: "showdelay", 30689 value: 200, 30690 validator: Lang.isNumber 30691 }, 30692
30693 "AUTO_DISMISS_DELAY": { 30694 key: "autodismissdelay", 30695 value: 5000, 30696 validator: Lang.isNumber 30697 }, 30698 30699 "HIDE_DELAY": { 30700 key: "hidedelay", 30701 value: 250, 30702 validator: Lang.isNumber 30703 }, 30704 30705 "TEXT": { 30706 key: "text", 30707 suppressEvent: true 30708 }, 30709 30710 "CONTAINER": { 30711 key: "container" 30712 }, 30713 30714 "DISABLED": { 30715 key: "disabled", 30716 value: false, 30717 suppressEvent: true 30718 }, 30719 30720 "XY_OFFSET": { 30721 key: "xyoffset", 30722 value: [0, 25], 30723 suppressEvent: true 30724 } 30725 }, 30726 30727 /** 30728 * Constant representing the name of the Tooltip's events 30729 * @property EVENT_TYPES 30730 * @private 30731 * @final 30732 * @type Object 30733 */ 30734 EVENT_TYPES = { 30735 "CONTEXT_MOUSE_OVER": "contextMouseOver", 30736 "CONTEXT_MOUSE_OUT": "contextMouseOut", 30737 "CONTEXT_TRIGGER": "contextTrigger" 30738 }; 30739 30740 /** 30741 * Constant representing the Tooltip CSS class 30742 * @property YAHOO.widget.Tooltip.CSS_TOOLTIP 30743 * @static 30744 * @final
30745 * @type String 30746 */ 30747 Tooltip.CSS_TOOLTIP = "yui-tt"; 30748 30749 function restoreOriginalWidth(sOriginalWidth, sForcedWidth) { 30750 30751 var oConfig = this.cfg, 30752 sCurrentWidth = oConfig.getProperty("width"); 30753 30754 if (sCurrentWidth == sForcedWidth) { 30755 oConfig.setProperty("width", sOriginalWidth); 30756 } 30757 } 30758 30759 /* 30760 changeContent event handler that sets a Tooltip instance's "width" 30761 configuration property to the value of its root HTML 30762 elements's offsetWidth if a specific width has not been set. 30763 */ 30764 30765 function setWidthToOffsetWidth(p_sType, p_aArgs) { 30766 30767 if ("_originalWidth" in this) { 30768 restoreOriginalWidth.call(this, this._originalWidth, this._forcedWidth); 30769 } 30770 30771 var oBody = document.body, 30772 oConfig = this.cfg, 30773 sOriginalWidth = oConfig.getProperty("width"), 30774 sNewWidth, 30775 oClone; 30776 30777 if ((!sOriginalWidth || sOriginalWidth == "auto") && 30778 (oConfig.getProperty("container") != oBody || 30779 oConfig.getProperty("x") >= Dom.getViewportWidth() || 30780 oConfig.getProperty("y") >= Dom.getViewportHeight())) { 30781 30782 oClone = this.element.cloneNode(true); 30783 oClone.style.visibility = "hidden"; 30784 oClone.style.top = "0px"; 30785 oClone.style.left = "0px"; 30786 30787 oBody.appendChild(oClone); 30788 30789 sNewWidth = (oClone.offsetWidth + "px"); 30790 30791 oBody.removeChild(oClone); 30792 oClone = null; 30793 30794 oConfig.setProperty("width", sNewWidth); 30795 oConfig.refireEvent("xy"); 30796 30797 this._originalWidth = sOriginalWidth || ""; 30798 this._forcedWidth = sNewWidth; 30799 } 30800 } 30801 30802 // "onDOMReady" that renders the ToolTip 30803 30804 function onDOMReady(p_sType, p_aArgs, p_oObject) { 30805 this.render(p_oObject); 30806 } 30807 30808 // "init" event handler that automatically renders the Tooltip 30809 30810 function onInit() { 30811 Event.onDOMReady(onDOMReady, this.cfg.getProperty("container"), this); 30812 } 30813 30814 YAHOO.extend(Tooltip, YAHOO.widget.Overlay, { 30815 30816 /** 30817 * The Tooltip initialization method. This method is automatically 30818 * called by the constructor. A Tooltip is automatically rendered by 30819 * the init method, and it also is set to be invisible by default, 30820 * and constrained to viewport by default as well. 30821 * @method init 30822 * @param {String} el The element ID representing the Tooltip <em>OR</em> 30823 * @param {HTMLElement} el The element representing the Tooltip 30824 * @param {Object} userConfig The configuration object literal 30825 * containing the configuration that should be set for this Tooltip. 30826 * See configuration documentation for more details. 30827 */ 30828 init: function (el, userConfig) { 30829 30830 this.logger = new YAHOO.widget.LogWriter(this.toString()); 30831 30832 Tooltip.superclass.init.call(this, el); 30833 30834 this.beforeInitEvent.fire(Tooltip); 30835 30836 Dom.addClass(this.element, Tooltip.CSS_TOOLTIP); 30837 30838 if (userConfig) { 30839 this.cfg.applyConfig(userConfig, true); 30840 } 30841 30842 this.cfg.queueProperty("visible", false); 30843 this.cfg.queueProperty("constraintoviewport", true); 30844 30845 this.setBody(""); 30846 30847 this.subscribe("changeContent", setWidthToOffsetWidth); 30848 this.subscribe("init", onInit); 30849 this.subscribe("render", this.onRender); 30850 30851 this.initEvent.fire(Tooltip); 30852 }, 30853 30854 /** 30855 * Initializes the custom events for Tooltip 30856 * @method initEvents 30857 */ 30858 initEvents: function () { 30859 30860 Tooltip.superclass.initEvents.call(this); 30861 var SIGNATURE = CustomEvent.LIST; 30862 30863 /** 30864 * CustomEvent fired when user mouses over a context element. Returning false from 30865 * a subscriber to this event will prevent the tooltip from being displayed for 30866 * the current context element. 30867 * 30868 * @event contextMouseOverEvent 30869 * @param {HTMLElement} context The context element which the user just moused over 30870 * @param {DOMEvent} e The DOM event object, associated with the mouse over 30871 */ 30872 this.contextMouseOverEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OVER);
30873 this.contextMouseOverEvent.signature = SIGNATURE; 30874 30875 /** 30876 * CustomEvent fired when the user mouses out of a context element. 30877 * 30878 * @event contextMouseOutEvent 30879 * @param {HTMLElement} context The context element which the user just moused out of 30880 * @param {DOMEvent} e The DOM event object, associated with the mouse out 30881 */ 30882 this.contextMouseOutEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OUT); 30883 this.contextMouseOutEvent.signature = SIGNATURE; 30884 30885 /** 30886 * CustomEvent fired just before the tooltip is displayed for the current context. 30887 * <p> 30888 * You can subscribe to this event if you need to set up the text for the 30889 * tooltip based on the context element for which it is about to be displayed. 30890 * </p> 30891 * <p>This event differs from the beforeShow event in following respects:</p> 30892 * <ol> 30893 * <li> 30894 * When moving from one context element to another, if the tooltip is not 30895 * hidden (the <code>hidedelay</code> is not reached), the beforeShow and Show events will not 30896 * be fired when the tooltip is displayed for the new context since it is already visible. 30897 * However the contextTrigger event is always fired before displaying the tooltip for 30898 * a new context. 30899 * </li> 30900 * <li> 30901 * The trigger event provides access to the context element, allowing you to 30902 * set the text of the tooltip based on context element for which the tooltip is 30903 * triggered. 30904 * </li> 30905 * </ol> 30906 * <p> 30907 * It is not possible to prevent the tooltip from being displayed 30908 * using this event. You can use the contextMouseOverEvent if you need to prevent 30909 * the tooltip from being displayed. 30910 * </p> 30911 * @event contextTriggerEvent 30912 * @param {HTMLElement} context The context element for which the tooltip is triggered 30913 */ 30914 this.contextTriggerEvent = this.createEvent(EVENT_TYPES.CONTEXT_TRIGGER); 30915 this.contextTriggerEvent.signature = SIGNATURE; 30916 }, 30917 30918 /** 30919 * Initializes the class's configurable properties which can be 30920 * changed using the Overlay's Config object (cfg). 30921 * @method initDefaultConfig 30922 */ 30923 initDefaultConfig: function () { 30924 30925 Tooltip.superclass.initDefaultConfig.call(this); 30926 30927 /** 30928 * Specifies whether the Tooltip should be kept from overlapping 30929 * its context element. 30930 * @config preventoverlap 30931 * @type Boolean 30932 * @default true 30933 */ 30934 this.cfg.addProperty(DEFAULT_CONFIG.PREVENT_OVERLAP.key, { 30935 value: DEFAULT_CONFIG.PREVENT_OVERLAP.value, 30936 validator: DEFAULT_CONFIG.PREVENT_OVERLAP.validator, 30937 supercedes: DEFAULT_CONFIG.PREVENT_OVERLAP.supercedes 30938 }); 30939 30940 /** 30941 * The number of milliseconds to wait before showing a Tooltip 30942 * on mouseover. 30943 * @config showdelay 30944 * @type Number 30945 * @default 200 30946 */ 30947 this.cfg.addProperty(DEFAULT_CONFIG.SHOW_DELAY.key, { 30948 handler: this.configShowDelay, 30949 value: 200, 30950 validator: DEFAULT_CONFIG.SHOW_DELAY.validator 30951 }); 30952 30953 /** 30954 * The number of milliseconds to wait before automatically 30955 * dismissing a Tooltip after the mouse has been resting on the 30956 * context element. 30957 * @config autodismissdelay 30958 * @type Number 30959 * @default 5000 30960 */ 30961 this.cfg.addProperty(DEFAULT_CONFIG.AUTO_DISMISS_DELAY.key, { 30962 handler: this.configAutoDismissDelay, 30963 value: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.value,
30964 validator: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.validator 30965 }); 30966 30967 /** 30968 * The number of milliseconds to wait before hiding a Tooltip 30969 * after mouseout. 30970 * @config hidedelay 30971 * @type Number 30972 * @default 250 30973 */ 30974 this.cfg.addProperty(DEFAULT_CONFIG.HIDE_DELAY.key, { 30975 handler: this.configHideDelay, 30976 value: DEFAULT_CONFIG.HIDE_DELAY.value, 30977 validator: DEFAULT_CONFIG.HIDE_DELAY.validator 30978 }); 30979 30980 /** 30981 * Specifies the Tooltip's text. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 30982 * @config text 30983 * @type HTML 30984 * @default null 30985 */ 30986 this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { 30987 handler: this.configText, 30988 suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent 30989 }); 30990 30991 /** 30992 * Specifies the container element that the Tooltip's markup 30993 * should be rendered into. 30994 * @config container 30995 * @type HTMLElement/String 30996 * @default document.body 30997 */ 30998 this.cfg.addProperty(DEFAULT_CONFIG.CONTAINER.key, { 30999 handler: this.configContainer, 31000 value: document.body 31001 }); 31002 31003 /** 31004 * Specifies whether or not the tooltip is disabled. Disabled tooltips 31005 * will not be displayed. If the tooltip is driven by the title attribute 31006 * of the context element, the title attribute will still be removed for 31007 * disabled tooltips, to prevent default tooltip behavior. 31008 * 31009 * @config disabled 31010 * @type Boolean 31011 * @default false 31012 */ 31013 this.cfg.addProperty(DEFAULT_CONFIG.DISABLED.key, { 31014 handler: this.configContainer, 31015 value: DEFAULT_CONFIG.DISABLED.value, 31016 supressEvent: DEFAULT_CONFIG.DISABLED.suppressEvent 31017 }); 31018 31019 /** 31020 * Specifies the XY offset from the mouse position, where the tooltip should be displayed, specified 31021 * as a 2 element array (e.g. [10, 20]); 31022 * 31023 * @config xyoffset 31024 * @type Array 31025 * @default [0, 25] 31026 */ 31027 this.cfg.addProperty(DEFAULT_CONFIG.XY_OFFSET.key, { 31028 value: DEFAULT_CONFIG.XY_OFFSET.value.concat(), 31029 supressEvent: DEFAULT_CONFIG.XY_OFFSET.suppressEvent 31030 }); 31031 31032 /** 31033 * Specifies the element or elements that the Tooltip should be 31034 * anchored to on mouseover. 31035 * @config context 31036 * @type HTMLElement[]/String[] 31037 * @default null 31038 */ 31039 31040 /** 31041 * String representing the width of the Tooltip. <em>Please note: 31042 * </em> As of version 2.3 if either no value or a value of "auto" 31043 * is specified, and the Toolip's "container" configuration property 31044 * is set to something other than <code>document.body</code> or 31045 * its "context" element resides outside the immediately visible 31046 * portion of the document, the width of the Tooltip will be 31047 * calculated based on the offsetWidth of its root HTML and set just 31048 * before it is made visible. The original value will be 31049 * restored when the Tooltip is hidden. This ensures the Tooltip is 31050 * rendered at a usable width. For more information see 31051 * YUILibrary bug #1685496 and YUILibrary 31052 * bug #1735423. 31053 * @config width
31054 * @type String 31055 * @default null 31056 */ 31057 31058 }, 31059 31060 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 31061 31062 /** 31063 * The default event handler fired when the "text" property is changed. 31064 * @method configText 31065 * @param {String} type The CustomEvent type (usually the property name) 31066 * @param {Object[]} args The CustomEvent arguments. For configuration 31067 * handlers, args[0] will equal the newly applied value for the property. 31068 * @param {Object} obj The scope object. For configuration handlers, 31069 * this will usually equal the owner. 31070 */ 31071 configText: function (type, args, obj) { 31072 var text = args[0]; 31073 if (text) { 31074 this.setBody(text); 31075 } 31076 }, 31077 31078 /** 31079 * The default event handler fired when the "container" property 31080 * is changed. 31081 * @method configContainer 31082 * @param {String} type The CustomEvent type (usually the property name) 31083 * @param {Object[]} args The CustomEvent arguments. For 31084 * configuration handlers, args[0] will equal the newly applied value 31085 * for the property. 31086 * @param {Object} obj The scope object. For configuration handlers, 31087 * this will usually equal the owner. 31088 */ 31089 configContainer: function (type, args, obj) { 31090 var container = args[0]; 31091 31092 if (typeof container == 'string') { 31093 this.cfg.setProperty("container", document.getElementById(container), true); 31094 } 31095 }, 31096 31097 /** 31098 * @method _removeEventListeners 31099 * @description Removes all of the DOM event handlers from the HTML 31100 * element(s) that trigger the display of the tooltip. 31101 * @protected 31102 */ 31103 _removeEventListeners: function () { 31104 31105 var aElements = this._context, 31106 nElements, 31107 oElement, 31108 i; 31109 31110 if (aElements) { 31111 nElements = aElements.length; 31112 if (nElements > 0) { 31113 i = nElements - 1; 31114 do { 31115 oElement = aElements[i]; 31116 Event.removeListener(oElement, "mouseover", this.onContextMouseOver); 31117 Event.removeListener(oElement, "mousemove", this.onContextMouseMove); 31118 Event.removeListener(oElement, "mouseout", this.onContextMouseOut); 31119 } 31120 while (i--); 31121 } 31122 } 31123 }, 31124 31125 /** 31126 * The default event handler fired when the "context" property 31127 * is changed. 31128 * @method configContext 31129 * @param {String} type The CustomEvent type (usually the property name) 31130 * @param {Object[]} args The CustomEvent arguments. For configuration 31131 * handlers, args[0] will equal the newly applied value for the property. 31132 * @param {Object} obj The scope object. For configuration handlers, 31133 * this will usually equal the owner. 31134 */ 31135 configContext: function (type, args, obj) { 31136 31137 var context = args[0], 31138 aElements, 31139 nElements, 31140 oElement, 31141 i; 31142 31143 if (context) { 31144 31145 // Normalize parameter into an array 31146 if (! (context instanceof Array)) { 31147 if (typeof context == "string") { 31148 this.cfg.setProperty("context", [document.getElementById(context)], true); 31149 } else { // Assuming this is an element 31150 this.cfg.setProperty("context", [context], true); 31151 } 31152 context = this.cfg.getProperty("context"); 31153 } 31154 31155 // Remove any existing mouseover/mouseout listeners 31156 this._removeEventListeners(); 31157 31158 // Add mouseover/mouseout listeners to context elements 31159 this._context = context; 31160 31161 aElements = this._context; 31162 31163 if (aElements) { 31164 nElements = aElements.length; 31165 if (nElements > 0) { 31166 i = nElements - 1; 31167 do { 31168 oElement = aElements[i]; 31169 Event.on(oElement, "mouseover", this.onContextMouseOver, this); 31170 Event.on(oElement, "mousemove", this.onContextMouseMove, this); 31171 Event.on(oElement, "mouseout", this.onContextMouseOut, this); 31172 } 31173 while (i--); 31174 } 31175 } 31176 } 31177 }, 31178 31179 // END BUILT-IN PROPERTY EVENT HANDLERS // 31180 31181 // BEGIN BUILT-IN DOM EVENT HANDLERS // 31182 31183 /** 31184 * The default event handler fired when the user moves the mouse while 31185 * over the context element. 31186 * @method onContextMouseMove 31187 * @param {DOMEvent} e The current DOM event 31188 * @param {Object} obj The object argument 31189 */ 31190 onContextMouseMove: function (e, obj) { 31191 obj.pageX = Event.getPageX(e); 31192 obj.pageY = Event.getPageY(e); 31193 }, 31194 31195 /** 31196 * The default event handler fired when the user mouses over the 31197 * context element. 31198 * @method onContextMouseOver 31199 * @param {DOMEvent} e The current DOM event 31200 * @param {Object} obj The object argument 31201 */ 31202 onContextMouseOver: function (e, obj) { 31203 var context = this; 31204 31205 if (context.title) { 31206 obj._tempTitle = context.title; 31207 context.title = ""; 31208 } 31209 31210 // Fire first, to honor disabled set in the listner 31211 if (obj.fireEvent("contextMouseOver", context, e) !== false && !
31211obj.cfg.getProperty("disabled")) { 31212 31213 // Stop the tooltip from being hidden (set on last mouseout) 31214 if (obj.hideProcId) { 31215 clearTimeout(obj.hideProcId); 31216 obj.logger.log("Clearing hide timer: " + obj.hideProcId, "time"); 31217 obj.hideProcId = null; 31218 } 31219 31220 Event.on(context, "mousemove", obj.onContextMouseMove, obj); 31221 31222 /** 31223 * The unique process ID associated with the thread responsible 31224 * for showing the Tooltip. 31225 * @type int 31226 */ 31227 obj.showProcId = obj.doShow(e, context); 31228 obj.logger.log("Setting show tooltip timeout: " + obj.showProcId, "time"); 31229 } 31230 }, 31231 31232 /** 31233 * The default event handler fired when the user mouses out of 31234 * the context element. 31235 * @method onContextMouseOut 31236 * @param {DOMEvent} e The current DOM event 31237 * @param {Object} obj The object argument 31238 */ 31239 onContextMouseOut: function (e, obj) { 31240 var el = this; 31241 31242 if (obj._tempTitle) { 31243 el.title = obj._tempTitle; 31244 obj._tempTitle = null; 31245 } 31246 31247 if (obj.showProcId) { 31248 clearTimeout(obj.showProcId); 31249 obj.logger.log("Clearing show timer: " + obj.showProcId, "time"); 31250 obj.showProcId = null; 31251 } 31252 31253 if (obj.hideProcId) { 31254 clearTimeout(obj.hideProcId); 31255 obj.logger.log("Clearing hide timer: " + obj.hideProcId, "time"); 31256 obj.hideProcId = null; 31257 } 31258 31259 obj.fireEvent("contextMouseOut", el, e); 31260 31261 obj.hideProcId = setTimeout(function () { 31262 obj.hide(); 31263 }, obj.cfg.getProperty("hidedelay")); 31264 }, 31265 31266 // END BUILT-IN DOM EVENT HANDLERS // 31267 31268 /** 31269 * Processes the showing of the Tooltip by setting the timeout delay 31270 * and offset of the Tooltip. 31271 * @method doShow 31272 * @param {DOMEvent} e The current DOM event 31273 * @param {HTMLElement} context The current context element 31274 * @return {Number} The process ID of the timeout function associated 31275 * with doShow 31276 */ 31277 doShow: function (e, context) { 31278 31279 var offset = this.cfg.getProperty("xyoffset"), 31280 xOffset = offset[0], 31281 yOffset = offset[1], 31282 me = this; 31283 31284 if (UA.opera && context.tagName && 31285 context.tagName.toUpperCase() == "A") { 31286 yOffset += 12; 31287 } 31288 31289 return setTimeout(function () { 31290 31291 var txt = me.cfg.getProperty("text"); 31292 31293 // title does not over-ride text 31294 if (me._tempTitle && (txt === "" || YAHOO.lang.isUndefined(txt) || YAHOO.lang.isNull(txt))) { 31295 me.setBody(me._tempTitle); 31296 } else { 31297 me.cfg.refireEvent("text"); 31298 } 31299 31300 me.logger.log("Show tooltip", "time"); 31301 me.moveTo(me.pageX + xOffset, me.pageY + yOffset); 31302 31303 if (me.cfg.getProperty("preventoverlap")) { 31304 me.preventOverlap(me.pageX, me.pageY); 31305 } 31306 31307 Event.removeListener(context, "mousemove", me.onContextMouseMove); 31308 31309 me.contextTriggerEvent.fire(context); 31310 31311 me.show(); 31312 31313 me.hideProcId = me.doHide(); 31314 me.logger.log("Hide tooltip time active: " + me.hideProcId, "time"); 31315 31316 }, this.cfg.getProperty("showdelay")); 31317 }, 31318 31319 /** 31320 * Sets the timeout for the auto-dismiss delay, which by default is 5 31321 * seconds, meaning that a tooltip will automatically dismiss itself 31322 * after 5 seconds of being displayed. 31323 * @method doHide 31324 */ 31325 doHide: function () { 31326 31327 var me = this; 31328 31329 me.logger.log("Setting hide tooltip timeout", "time"); 31330 31331 return setTimeout(function () { 31332 31333 me.logger.log("Hide tooltip", "time"); 31334 me.hide(); 31335 31336 }, this.cfg.getProperty("autodismissdelay")); 31337 31338 }, 31339 31340 /** 31341 * Fired when the Tooltip is moved, this event handler is used to 31342 * prevent the Tooltip from overlapping with its context element. 31343 * @method preventOverlay 31344 * @param {Number} pageX The x coordinate position of the mouse pointer 31345 * @param {Number} pageY The y coordinate position of the mouse pointer 31346 */ 31347 preventOverlap: function (pageX, pageY) { 31348 31349 var height = this.element.offsetHeight, 31350 mousePoint = new YAHOO.util.Point(pageX, pageY), 31351 elementRegion = Dom.getRegion(this.element); 31352 31353 elementRegion.top -= 5; 31354 elementRegion.left -= 5; 31355 elementRegion.right += 5; 31356 elementRegion.bottom += 5; 31357 31358 this.logger.log("context " + elementRegion, "ttip"); 31359 this.logger.log("mouse " + mousePoint, "ttip"); 31360 31361 if (elementRegion.contains(mousePoint)) { 31362 this.logger.log("OVERLAP", "warn"); 31363 this.cfg.setProperty("y", (pageY - height - 5)); 31364 } 31365 }, 31366 31367 31368 /** 31369 * @method onRender
31370 * @description "render" event handler for the Tooltip. 31371 * @param {String} p_sType String representing the name of the event 31372 * that was fired. 31373 * @param {Array} p_aArgs Array of arguments sent when the event 31374 * was fired. 31375 */ 31376 onRender: function (p_sType, p_aArgs) { 31377 31378 function sizeShadow() { 31379 31380 var oElement = this.element, 31381 oShadow = this.underlay; 31382 31383 if (oShadow) { 31384 oShadow.style.width = (oElement.offsetWidth + 6) + "px"; 31385 oShadow.style.height = (oElement.offsetHeight + 1) + "px"; 31386 } 31387 31388 } 31389 31390 function addShadowVisibleClass() { 31391 Dom.addClass(this.underlay, "yui-tt-shadow-visible"); 31392 31393 if (UA.ie) { 31394 this.forceUnderlayRedraw(); 31395 } 31396 } 31397 31398 function removeShadowVisibleClass() { 31399 Dom.removeClass(this.underlay, "yui-tt-shadow-visible"); 31400 } 31401 31402 function createShadow() { 31403 31404 var oShadow = this.underlay, 31405 oElement, 31406 Module, 31407 nIE, 31408 me; 31409 31410 if (!oShadow) { 31411 31412 oElement = this.element; 31413 Module = YAHOO.widget.Module; 31414 nIE = UA.ie; 31415 me = this; 31416 31417 if (!m_oShadowTemplate) { 31418 m_oShadowTemplate = document.createElement("div"); 31419 m_oShadowTemplate.className = "yui-tt-shadow"; 31420 } 31421 31422 oShadow = m_oShadowTemplate.cloneNode(false); 31423 31424 oElement.appendChild(oShadow); 31425 31426 this.underlay = oShadow; 31427 31428 // Backward compatibility, even though it's probably 31429 // intended to be "private", it isn't marked as such in the api docs 31430 this._shadow = this.underlay; 31431 31432 addShadowVisibleClass.call(this); 31433 31434 this.subscribe("beforeShow", addShadowVisibleClass); 31435 this.subscribe("hide", removeShadowVisibleClass); 31436 31437 if (bIEQuirks) { 31438 window.setTimeout(function () { 31439 sizeShadow.call(me); 31440 }, 0); 31441 31442 this.cfg.subscribeToConfigEvent("width", sizeShadow); 31443 this.cfg.subscribeToConfigEvent("height", sizeShadow); 31444 this.subscribe("changeContent", sizeShadow); 31445 31446 Module.textResizeEvent.subscribe(sizeShadow, this, true); 31447 this.subscribe("destroy", function () { 31448 Module.textResizeEvent.unsubscribe(sizeShadow, this); 31449 }); 31450 } 31451 } 31452 } 31453 31454 function onBeforeShow() { 31455 createShadow.call(this); 31456 this.unsubscribe("beforeShow", onBeforeShow); 31457 } 31458 31459 if (this.cfg.getProperty("visible")) { 31460 createShadow.call(this); 31461 } else { 31462 this.subscribe("beforeShow", onBeforeShow); 31463 } 31464 31465 }, 31466 31467 /** 31468 * Forces the underlay element to be repainted, through the application/removal 31469 * of a yui-force-redraw class to the underlay element. 31470 * 31471 * @method forceUnderlayRedraw 31472 */ 31473 forceUnderlayRedraw : function() { 31474 var tt = this; 31475 Dom.addClass(tt.underlay, "yui-force-redraw"); 31476 setTimeout(function() {Dom.removeClass(tt.underlay, "yui-force-redraw");}, 0); 31477 }, 31478 31479 /** 31480 * Removes the Tooltip element from the DOM and sets all child 31481 * elements to null. 31482 * @method destroy 31483 */ 31484 destroy: function () { 31485 31486 // Remove any existing mouseover/mouseout listeners 31487 this._removeEventListeners(); 31488 31489 Tooltip.superclass.destroy.call(this); 31490 31491 }, 31492 31493 /** 31494 * Returns a string representation of the object. 31495 * @method toString 31496 * @return {String} The string representation of the Tooltip 31497 */ 31498 toString: function () { 31499 return "Tooltip " + this.id; 31500 } 31501 31502 }); 31503 31504}()); 31505(function () { 31506 31507 /** 31508 * Panel is an implementation of Overlay that behaves like an OS window, 31509 * with a draggable header and an optional close icon at the top right. 31510 * @namespace YAHOO.widget 31511 * @class Panel 31512 * @extends YAHOO.widget.Overlay 31513 * @constructor 31514 * @param {String} el The element ID representing the Panel <em>OR</em> 31515 * @param {HTMLElement} el The element representing the Panel 31516 * @param {Object} userConfig The configuration object literal containing 31517 * the configuration that should be set for this Panel. See configuration 31518 * documentation for more details. 31519 */ 31520 YAHOO.widget.Panel = function (el, userConfig) { 31521 YAHOO.widget.Panel.superclass.constructor.call(this, el, userConfig); 31522 }; 31523 31524 var _currentModal = null; 31525 31526 var Lang = YAHOO.lang, 31527 Util = YAHOO.util, 31528 Dom = Util.Dom, 31529 Event = Util.Event, 31530 CustomEvent = Util.CustomEvent, 31531 KeyListener = YAHOO.util.KeyListener, 31532 Config = Util.Config, 31533 Overlay = YAHOO.widget.Overlay, 31534 Panel = YAHOO.widget.Panel, 31535 UA = YAHOO.env.ua, 31536 31537 bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), 31538 31539 m_oMaskTemplate, 31540 m_oUnderlayTemplate, 31541 m_oCloseIconTemplate, 31542 31543 /** 31544 * Constant representing the name of the Panel's events 31545 * @property EVENT_TYPES 31546 * @private 31547 * @final 31548 * @type Object 31549 */ 31550 EVENT_TYPES = { 31551 "BEFORE_SHOW_MASK" : "beforeShowMask", 31552 "BEFORE_HIDE_MASK" : "beforeHideMask", 31553 "SHOW_MASK": "showMask", 31554 "HIDE_MASK": "hideMask", 31555 "DRAG": "drag" 31556 }, 31557 31558 /** 31559 * Constant representing the Panel's configuration properties 31560 * @property DEFAULT_CONFIG 31561 * @private 31562 * @final 31563 * @type Object 31564 */ 31565 DEFAULT_CONFIG = { 31566 31567 "CLOSE": { 31568 key: "close", 31569 value: true, 31570 validator: Lang.isBoolean, 31571 supercedes: ["visible"] 31572 }, 31573 31574 "DRAGGABLE": { 31575 key: "draggable", 31576 value: (Util.DD ? true : false), 31577 validator: Lang.isBoolean, 31578 supercedes: ["visible"] 31579 }, 31580 31581 "DRAG_ONLY" : { 31582 key: "dragonly", 31583 value: false, 31584 validator: Lang.isBoolean, 31585 supercedes: ["draggable"] 31586 }, 31587 31588 "UNDERLAY": { 31589 key: "underlay", 31590 value: "shadow", 31591 supercedes: ["visible"] 31592 }, 31593 31594 "MODAL": { 31595 key: "modal", 31596 value: false, 31597 validator: Lang.isBoolean, 31598 supercedes: ["visible", "zindex"] 31599 }, 31600 31601 "KEY_LISTENERS": { 31602 key: "keylisteners", 31603 suppressEvent: true, 31604 supercedes: ["visible"] 31605 }, 31606 31607 "STRINGS" : { 31608 key: "strings", 31609 supercedes: ["close"], 31610 validator: Lang.isObject, 31611 value: { 31612 close: "Close" 31613 } 31614 } 31615 }; 31616 31617 /** 31618 * Constant representing the default CSS class used for a Panel 31619 * @property YAHOO.widget.Panel.CSS_PANEL 31620 * @static 31621 * @final
31622 * @type String 31623 */ 31624 Panel.CSS_PANEL = "yui-panel"; 31625 31626 /** 31627 * Constant representing the default CSS class used for a Panel's 31628 * wrapping container 31629 * @property YAHOO.widget.Panel.CSS_PANEL_CONTAINER 31630 * @static 31631 * @final 31632 * @type String 31633 */ 31634 Panel.CSS_PANEL_CONTAINER = "yui-panel-container"; 31635 31636 /** 31637 * Constant representing the default set of focusable elements 31638 * on the pagewhich Modal Panels will prevent access to, when 31639 * the modal mask is displayed 31640 * 31641 * @property YAHOO.widget.Panel.FOCUSABLE 31642 * @static 31643 * @type Array 31644 */ 31645 Panel.FOCUSABLE = [ 31646 "a", 31647 "button", 31648 "select", 31649 "textarea", 31650 "input", 31651 "iframe" 31652 ]; 31653 31654 // Private CustomEvent listeners 31655 31656 /* 31657 "beforeRender" event handler that creates an empty header for a Panel 31658 instance if its "draggable" configuration property is set to "true" 31659 and no header has been created. 31660 */ 31661 31662 function createHeader(p_sType, p_aArgs) { 31663 if (!this.header && this.cfg.getProperty("draggable")) { 31664 this.setHeader(" "); 31665 } 31666 } 31667 31668 /* 31669 "hide" event handler that sets a Panel instance's "width" 31670 configuration property back to its original value before 31671 "setWidthToOffsetWidth" was called. 31672 */ 31673 31674 function restoreOriginalWidth(p_sType, p_aArgs, p_oObject) { 31675 31676 var sOriginalWidth = p_oObject[0], 31677 sNewWidth = p_oObject[1], 31678 oConfig = this.cfg, 31679 sCurrentWidth = oConfig.getProperty("width"); 31680 31681 if (sCurrentWidth == sNewWidth) { 31682 oConfig.setProperty("width", sOriginalWidth); 31683 } 31684 31685 this.unsubscribe("hide", restoreOriginalWidth, p_oObject); 31686 } 31687 31688 /* 31689 "beforeShow" event handler that sets a Panel instance's "width" 31690 configuration property to the value of its root HTML 31691 elements's offsetWidth 31692 */ 31693 31694 function setWidthToOffsetWidth(p_sType, p_aArgs) { 31695 31696 var oConfig, 31697 sOriginalWidth, 31698 sNewWidth; 31699 31700 if (bIEQuirks) { 31701 31702 oConfig = this.cfg; 31703 sOriginalWidth = oConfig.getProperty("width"); 31704 31705 if (!sOriginalWidth || sOriginalWidth == "auto") { 31706 31707 sNewWidth = (this.element.offsetWidth + "px"); 31708 31709 oConfig.setProperty("width", sNewWidth); 31710 31711 this.subscribe("hide", restoreOriginalWidth, 31712 [(sOriginalWidth || ""), sNewWidth]); 31713 31714 } 31715 } 31716 } 31717 31718 YAHOO.extend(Panel, Overlay, { 31719 31720 /** 31721 * The Overlay initialization method, which is executed for Overlay and 31722 * all of its subclasses. This method is automatically called by the 31723 * constructor, and sets up all DOM references for pre-existing markup, 31724 * and creates required markup if it is not already present. 31725 * @method init 31726 * @param {String} el The element ID representing the Overlay <em>OR</em> 31727 * @param {HTMLElement} el The element representing the Overlay 31728 * @param {Object} userConfig The configuration object literal 31729 * containing the configuration that should be set for this Overlay. 31730 * See configuration documentation for more details. 31731 */ 31732 init: function (el, userConfig) { 31733 /* 31734 Note that we don't pass the user config in here yet because 31735 we only want it executed once, at the lowest subclass level 31736 */ 31737 31738 Panel.superclass.init.call(this, el/*, userConfig*/); 31739 31740 this.beforeInitEvent.fire(Panel); 31741 31742 Dom.addClass(this.element, Panel.CSS_PANEL); 31743 31744 this.buildWrapper(); 31745 31746 if (userConfig) { 31747 this.cfg.applyConfig(userConfig, true); 31748 } 31749 31750 this.subscribe("showMask", this._addFocusHandlers); 31751 this.subscribe("hideMask", this._removeFocusHandlers); 31752 this.subscribe("beforeRender", createHeader); 31753 31754 this.subscribe("render", function() { 31755 this.setFirstLastFocusable(); 31756 this.subscribe("changeContent", this.setFirstLastFocusable); 31757 }); 31758 31759 this.subscribe("show", this._focusOnShow); 31760 31761 this.initEvent.fire(Panel); 31762 }, 31763 31764 /** 31765 * @method _onElementFocus 31766 * @private 31767 * 31768 * "focus" event handler for a focuable element. Used to automatically 31769 * blur the element when it receives focus to ensure that a Panel 31770 * instance's modality is not compromised. 31771 * 31772 * @param {Event} e The DOM event object 31773 */ 31774 _onElementFocus : function(e){ 31775 31776 if(_currentModal === this) { 31777 31778 var target = Event.getTarget(e), 31779 doc = document.documentElement, 31780 insideDoc = (target !== doc && target !== window); 31781 31782 // mask and documentElement checks added for IE, which focu
31782ses on the mask when it's clicked on, and focuses on 31783 // the documentElement, when the document scrollbars are clicked on 31784 if (insideDoc && target !== this.element && target !== this.mask && !Dom.isAncestor(this.element, target)) { 31785 try { 31786 this._focusFirstModal(); 31787 } catch(err){ 31788 // Just in case we fail to focus 31789 try { 31790 if (insideDoc && target !== document.body) { 31791 target.blur(); 31792 } 31793 } catch(err2) { } 31794 } 31795 } 31796 } 31797 }, 31798 31799 /** 31800 * Focuses on the first element if present, otherwise falls back to the focus mechanisms used for 31801 * modality. This method does not try/catch focus failures. The caller is responsible for catching exceptions, 31802 * and taking remedial measures. 31803 * 31804 * @method _focusFirstModal 31805 */ 31806 _focusFirstModal : function() { 31807 var el = this.firstElement; 31808 if (el) { 31809 el.focus(); 31810 } else { 31811 if (this._modalFocus) { 31812 this._modalFocus.focus(); 31813 } else { 31814 this.innerElement.focus(); 31815 } 31816 } 31817 }, 31818 31819 /** 31820 * @method _addFocusHandlers 31821 * @protected 31822 * 31823 * "showMask" event handler that adds a "focus" event handler to all 31824 * focusable elements in the document to enforce a Panel instance's 31825 * modality from being compromised. 31826 * 31827 * @param p_sType {String} Custom event type 31828 * @param p_aArgs {Array} Custom event arguments 31829 */ 31830 _addFocusHandlers: function(p_sType, p_aArgs) { 31831 if (!this.firstElement) { 31832 if (UA.webkit || UA.opera) { 31833 if (!this._modalFocus) { 31834 this._createHiddenFocusElement(); 31835 } 31836 } else { 31837 this.innerElement.tabIndex = 0; 31838 } 31839 } 31840 this._setTabLoop(this.firstElement, this.lastElement); 31841 Event.onFocus(document.documentElement, this._onElementFocus, this, true); 31842 _currentModal = this; 31843 }, 31844 31845 /** 31846 * Creates a hidden focusable element, used to focus on, 31847 * to enforce modality for browsers in which focus cannot 31848 * be applied to the container box. 31849 * 31850 * @method _createHiddenFocusElement 31851 * @private 31852 */ 31853 _createHiddenFocusElement : function() { 31854 var e = document.createElement("button"); 31855 e.style.height = "1px"; 31856 e.style.width = "1px"; 31857 e.style.position = "absolute"; 31858 e.style.left = "-10000em"; 31859 e.style.opacity = 0; 31860 e.tabIndex = -1; 31861 this.innerElement.appendChild(e); 31862 this._modalFocus = e; 31863 }, 31864 31865 /** 31866 * @method _removeFocusHandlers 31867 * @protected 31868 * 31869 * "hideMask" event handler that removes all "focus" event handlers added 31870 * by the "addFocusEventHandlers" method. 31871 * 31872 * @param p_sType {String} Event type 31873 * @param p_aArgs {Array} Event Arguments 31874 */ 31875 _removeFocusHandlers: function(p_sType, p_aArgs) { 31876 Event.removeFocusListener(document.documentElement, this._onElementFocus, this); 31877 31878 if (_currentModal == this) { 31879 _currentModal = null; 31880 } 31881 }, 31882 31883 /** 31884 * Focus handler for the show event 31885 * 31886 * @method _focusOnShow 31887 * @param {String} type Event Type 31888 * @param {Array} args Event arguments 31889 * @param {Object} obj Additional data 31890 */ 31891 _focusOnShow : function(type, args, obj) { 31892 31893 if (args && args[1]) { 31894 Event.stopEvent(args[1]); 31895 } 31896 31897 if (!this.focusFirst(type, args, obj)) { 31898 if (this.cfg.getProperty("modal")) { 31899 this._focusFirstModal(); 31900 } 31901 } 31902 }, 31903 31904 /** 31905 * Sets focus to the first element in the Panel. 31906 *
31907 * @method focusFirst 31908 * @return {Boolean} true, if successfully focused, false otherwise 31909 */ 31910 focusFirst: function (type, args, obj) { 31911 var el = this.firstElement, focused = false; 31912 31913 if (args && args[1]) { 31914 Event.stopEvent(args[1]); 31915 } 31916 31917 if (el) { 31918 try { 31919 el.focus(); 31920 focused = true; 31921 } catch(err) { 31922 // Ignore 31923 } 31924 } 31925 31926 return focused; 31927 }, 31928 31929 /** 31930 * Sets focus to the last element in the Panel. 31931 * 31932 * @method focusLast 31933 * @return {Boolean} true, if successfully focused, false otherwise 31934 */ 31935 focusLast: function (type, args, obj) { 31936 var el = this.lastElement, focused = false; 31937 31938 if (args && args[1]) { 31939 Event.stopEvent(args[1]); 31940 } 31941 31942 if (el) { 31943 try { 31944 el.focus(); 31945 focused = true; 31946 } catch(err) { 31947 // Ignore 31948 } 31949 } 31950 31951 return focused; 31952 }, 31953 31954 /** 31955 * Protected internal method for setTabLoop, which can be used by 31956 * subclasses to jump in and modify the arguments passed in if required. 31957 * 31958 * @method _setTabLoop 31959 * @param {HTMLElement} firstElement 31960 * @param {HTMLElement} lastElement 31961 * @protected 31962 * 31963 */ 31964 _setTabLoop : function(firstElement, lastElement) { 31965 this.setTabLoop(firstElement, lastElement); 31966 }, 31967 31968 /** 31969 * Sets up a tab, shift-tab loop between the first and last elements 31970 * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener 31971 * instance properties, which are reset everytime this method is invoked. 31972 * 31973 * @method setTabLoop 31974 * @param {HTMLElement} firstElement 31975 * @param {HTMLElement} lastElement 31976 * 31977 */ 31978 setTabLoop : function(firstElement, lastElement) { 31979 31980 var backTab = this.preventBackTab, tab = this.preventTabOut, 31981 showEvent = this.showEvent, hideEvent = this.hideEvent; 31982 31983 if (backTab) { 31984 backTab.disable(); 31985 showEvent.unsubscribe(backTab.enable, backTab); 31986 hideEvent.unsubscribe(backTab.disable, backTab); 31987 backTab = this.preventBackTab = null; 31988 } 31989 31990 if (tab) { 31991 tab.disable(); 31992 showEvent.unsubscribe(tab.enable, tab); 31993 hideEvent.unsubscribe(tab.disable,tab); 31994 tab = this.preventTabOut = null; 31995 } 31996 31997 if (firstElement) { 31998 this.preventBackTab = new KeyListener(firstElement, 31999 {shift:true, keys:9}, 32000 {fn:this.focusLast, scope:this, correctScope:true} 32001 ); 32002 backTab = this.preventBackTab; 32003 32004 showEvent.subscribe(backTab.enable, backTab, true); 32005 hideEvent.subscribe(backTab.disable,backTab, true); 32006 } 32007 32008 if (lastElement) { 32009 this.preventTabOut = new KeyListener(lastElement, 32010 {shift:false, keys:9}, 32011 {fn:this.focusFirst, scope:this, correctScope:true} 32012 ); 32013 tab = this.preventTabOut; 32014 32015 showEvent.subscribe(tab.enable, tab, true); 32016 hideEvent.subscribe(tab.disable,tab, true); 32017 } 32018 }, 32019 32020 /** 32021 * Returns an array of the currently focusable items which reside within 32022 * Panel. The set of focusable elements the method looks for are defined 32023 * in the Panel.FOCUSABLE static property 32024 * 32025 * @method getFocusableElements 32026 * @param {HTMLElement} root element to start from. 32027 */ 32028 getFocusableElements : function(root) { 32029 32030 root = root || this.innerElement; 32031 32032 var focusable = {}, panel = this; 32033 for (var i = 0; i < Panel.FOCUSABLE.length; i++) { 32034 focusable[Panel.FOCUSABLE[i]] = true; 32035 } 32036 32037 // Not looking by Tag, since we want elements in DOM order 32038 32039 return Dom.getElementsBy(function(el) { return panel._testIfFocusable(el, focusable); }, null, root); 32040 }, 32041 32042 /** 32043 * This is the test method used by getFocusableElements, to determine which elements to 32044 * include in the focusable elements list. Users may override this to customize behavior. 32045 * 32046 * @method _testIfFocusable 32047 * @param {Object} el The element being tested 32048 * @param {Object} focusable The hash of known focusable elements, created by an array-to-map operation on Panel.FOCUSABLE 32049 * @protected 32050 */ 32051 _testIfFocusable: function(el, focusable) { 32052 if (el.focus && el.type !== "hidden" && !el.disabled && focusable[el.tagName.toLowerCase()]) { 32053 return true; 32054 } 32055 return false;
32056 }, 32057 32058 /** 32059 * Sets the firstElement and lastElement instance properties 32060 * to the first and last focusable elements in the Panel. 32061 * 32062 * @method setFirstLastFocusable 32063 */ 32064 setFirstLastFocusable : function() { 32065 32066 this.firstElement = null; 32067 this.lastElement = null; 32068 32069 var elements = this.getFocusableElements(); 32070 this.focusableElements = elements; 32071 32072 if (elements.length > 0) { 32073 this.firstElement = elements[0]; 32074 this.lastElement = elements[elements.length - 1]; 32075 } 32076 32077 if (this.cfg.getProperty("modal")) { 32078 this._setTabLoop(this.firstElement, this.lastElement); 32079 } 32080 }, 32081 32082 /** 32083 * Initializes the custom events for Module which are fired 32084 * automatically at appropriate times by the Module class. 32085 */ 32086 initEvents: function () { 32087 Panel.superclass.initEvents.call(this); 32088 32089 var SIGNATURE = CustomEvent.LIST; 32090 32091 /** 32092 * CustomEvent fired after the modality mask is shown 32093 * @event showMaskEvent 32094 */ 32095 this.showMaskEvent = this.createEvent(EVENT_TYPES.SHOW_MASK); 32096 this.showMaskEvent.signature = SIGNATURE; 32097 32098 /** 32099 * CustomEvent fired before the modality mask is shown. Subscribers can return false to prevent the 32100 * mask from being shown 32101 * @event beforeShowMaskEvent 32102 */ 32103 this.beforeShowMaskEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW_MASK); 32104 this.beforeShowMaskEvent.signature = SIGNATURE; 32105 32106 /** 32107 * CustomEvent fired after the modality mask is hidden 32108 * @event hideMaskEvent 32109 */ 32110 this.hideMaskEvent = this.createEvent(EVENT_TYPES.HIDE_MASK); 32111 this.hideMaskEvent.signature = SIGNATURE; 32112 32113 /** 32114 * CustomEvent fired before the modality mask is hidden. Subscribers can return false to prevent the 32115 * mask from being hidden 32116 * @event beforeHideMaskEvent 32117 */ 32118 this.beforeHideMaskEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE_MASK); 32119 this.beforeHideMaskEvent.signature = SIGNATURE; 32120 32121 /** 32122 * CustomEvent when the Panel is dragged 32123 * @event dragEvent 32124 */ 32125 this.dragEvent = this.createEvent(EVENT_TYPES.DRAG); 32126 this.dragEvent.signature = SIGNATURE; 32127 }, 32128 32129 /** 32130 * Initializes the class's configurable properties which can be changed 32131 * using the Panel's Config object (cfg). 32132 * @method initDefaultConfig 32133 */ 32134 initDefaultConfig: function () { 32135 Panel.superclass.initDefaultConfig.call(this); 32136 32137 // Add panel config properties // 32138 32139 /** 32140 * True if the Panel should display a "close" button 32141 * @config close 32142 * @type Boolean 32143 * @default true 32144 */ 32145 this.cfg.addProperty(DEFAULT_CONFIG.CLOSE.key, { 32146 handler: this.configClose, 32147 value: DEFAULT_CONFIG.CLOSE.value, 32148 validator: DEFAULT_CONFIG.CLOSE.validator, 32149 supercedes: DEFAULT_CONFIG.CLOSE.supercedes 32150 }); 32151 32152 /** 32153 * Boolean specifying if the Panel should be draggable. The default 32154 * value is "true" if the Drag and Drop utility is included, 32155 * otherwise it is "false." <strong>PLEASE NOTE:</strong> There is a 32156 * known issue in IE 6 (Strict Mode and Quirks Mode) and IE 7 32157 * (Quirks Mode) where Panels that either don't have a value set for 32158 * their "width" configuration property, or their "width" 32159 * configuration property is set to "auto" will only be draggable by 32160 * placing the mouse on the text of the Panel's header element. 32161 * To fix this bug, draggable Panels missing a value for their 32162 * "width" configuration property, or whose "width" configuration 32163 * property is set to "auto" will have it set to the value of 32164 * their root HTML element's offsetWidth before they are made 32165 * visible. The calculated width is then removed when the Panel is 32166 * hidden. <em>This fix is only applied to draggable Panels in IE 6 32167 * (Strict Mode and Quirks Mode) and IE 7 (Quirks Mode)</em>. For 32168 * more information on this issue see: 32169 * YUILibrary bugs #1726972 and #1589210. 32170 * @config draggable 32171 * @type Boolean 32172 * @default true 32173 */ 32174 this.cfg.addProperty(DEFAULT_CONFIG.DRAGGABLE.key, { 32175 handler: this.configDraggable, 32176 value: (Util.DD) ? true : false, 32177 validator: DEFAULT_CONFIG.DRAGGABLE.validator, 32178 supercedes: DEFAULT_CONFIG.DRAGGABLE.supercedes 32179 }); 32180 32181 /** 32182 * Boolean specifying if the draggable Panel should be drag only, not interacting with drop 32183 * targets on the page. 32184 * <p> 32185 * When set to true, draggable Panels will not check to see if they are over drop targets, 32186 * or fire the DragDrop events required to support drop target interaction (onDragEnter, 32187 * onDragOver, onDragOut, onDragDrop etc.). 32188 * If the Panel is not designed to be dropped on any target elements on the page, then this 32189 * flag can be set to true to improve performance. 32190 * </p> 32191 * <p> 32192 * When set to false, all drop target related events will be fired. 32193 * </p> 32194 * <p> 32195 * The property is set to false by default to maintain backwards compatibility but should be 32196 * set to true if drop target interaction is not required for the Panel, to improve performance.</p> 32197 * 32198 * @config dragOnly 32199 * @type Boolean 32200 * @default false 32201 */ 32202 this.cfg.addProperty(DEFAULT_CONFIG.DRAG_ONLY.key, { 32203 value: DEFAULT_CONFIG.DRAG_ONLY.value, 32204 validator: DEFAULT_CONFIG.DRAG_ONLY.validator, 32205 supercedes: DEFAULT_CONFIG.DRAG_ONLY.supercedes 32206 }); 32207 32208 /** 32209 * Sets the type of underlay to display for the Panel. Valid values 32210 * are "shadow," "matte," and "none". <strong>PLEASE NOTE:</strong> 32211 * The creation of the underlay element is deferred until the Panel 32212 * is initially made visible. For Gecko-based browsers on Mac 32213 * OS X the underlay elment is always created as it is used as a 32214 * shim to prevent Aqua scrollbars below a Panel instance from poking 32215 * through it (See YUILibrary bug #1723530). 32216 * @config underlay
32217 * @type String 32218 * @default shadow 32219 */ 32220 this.cfg.addProperty(DEFAULT_CONFIG.UNDERLAY.key, { 32221 handler: this.configUnderlay, 32222 value: DEFAULT_CONFIG.UNDERLAY.value, 32223 supercedes: DEFAULT_CONFIG.UNDERLAY.supercedes 32224 }); 32225 32226 /** 32227 * True if the Panel should be displayed in a modal fashion, 32228 * automatically creating a transparent mask over the document that 32229 * will not be removed until the Panel is dismissed. 32230 * @config modal 32231 * @type Boolean 32232 * @default false 32233 */ 32234 this.cfg.addProperty(DEFAULT_CONFIG.MODAL.key, { 32235 handler: this.configModal, 32236 value: DEFAULT_CONFIG.MODAL.value, 32237 validator: DEFAULT_CONFIG.MODAL.validator, 32238 supercedes: DEFAULT_CONFIG.MODAL.supercedes 32239 }); 32240 32241 /** 32242 * A KeyListener (or array of KeyListeners) that will be enabled 32243 * when the Panel is shown, and disabled when the Panel is hidden. 32244 * @config keylisteners 32245 * @type YAHOO.util.KeyListener[] 32246 * @default null 32247 */ 32248 this.cfg.addProperty(DEFAULT_CONFIG.KEY_LISTENERS.key, { 32249 handler: this.configKeyListeners, 32250 suppressEvent: DEFAULT_CONFIG.KEY_LISTENERS.suppressEvent, 32251 supercedes: DEFAULT_CONFIG.KEY_LISTENERS.supercedes 32252 }); 32253 32254 /** 32255 * UI Strings used by the Panel. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 32256 * 32257 * @config strings 32258 * @type Object 32259 * @default An object literal with the properties shown below: 32260 * <dl> 32261 * <dt>close</dt><dd><em>HTML</em> : The markup to use as the label for the close icon. Defaults to "Close".</dd> 32262 * </dl> 32263 */ 32264 this.cfg.addProperty(DEFAULT_CONFIG.STRINGS.key, { 32265 value:DEFAULT_CONFIG.STRINGS.value, 32266 handler:this.configStrings, 32267 validator:DEFAULT_CONFIG.STRINGS.validator, 32268 supercedes:DEFAULT_CONFIG.STRINGS.supercedes 32269 }); 32270 }, 32271 32272 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 32273 32274 /** 32275 * The default event handler fired when the "close" property is changed. 32276 * The method controls the appending or hiding of the close icon at the 32277 * top right of the Panel. 32278 * @method configClose 32279 * @param {String} type The CustomEvent type (usually the property name) 32280 * @param {Object[]} args The CustomEvent arguments. For configuration 32281 * handlers, args[0] will equal the newly applied value for the property. 32282 * @param {Object} obj The scope object. For configuration handlers, 32283 * this will usually equal the owner. 32284 */ 32285 configClose: function (type, args, obj) { 32286 32287 var val = args[0], 32288 oClose = this.close, 32289 strings = this.cfg.getProperty("strings"), 32290 fc; 32291 32292 if (val) { 32293 if (!oClose) { 32294 32295 if (!m_oCloseIconTemplate) { 32296 m_oCloseIconTemplate = document.createElement("a"); 32297 m_oCloseIconTemplate.className = "container-close"; 32298 m_oCloseIconTemplate.href = "#"; 32299 } 32300 32301 oClose = m_oCloseIconTemplate.cloneNode(true); 32302 32303 fc = this.innerElement.firstChild; 32304 32305 if (fc) { 32306 this.innerElement.insertBefore(oClose, fc); 32307 } else { 32308 this.innerElement.appendChild(oClose); 32309 } 32310 32311 oClose.innerHTML = (strings && strings.close) ? strings.close : " "; 32312 32313 Event.on(oClose, "click", this._doClose, this, true); 32314 32315 this.close = oClose; 32316 32317 } else { 32318 oClose.style.display = "block"; 32319 } 32320 32321 } else { 32322 if (oClose) { 32323 oClose.style.display = "none"; 32324 } 32325 } 32326 32327 }, 32328 32329 /** 32330 * Event handler for the close icon 32331 * 32332 * @method _doClose 32333 * @protected 32334 * 32335 * @param {DOMEvent} e 32336 */ 32337 _doClose : function (e) { 32338 Event.preventDefault(e); 32339 this.hide(); 32340 }, 32341 32342 /** 32343 * The default event handler fired when the "draggable" property 32344 * is changed. 32345 * @method configDraggable 32346 * @param {String} type The CustomEvent type (usually the property name) 32347 * @param {Object[]} args The CustomEvent arguments. For configuration 32348 * handlers, args[0] will equal the newly applied value for the property. 32349 * @param {Object} obj The scope object. For configuration handlers, 32350 * this will usually equal the owner. 32351 */ 32352 configDraggable: function (type, args, obj) { 32353 var val = args[0]; 32354 32355 if (val) { 32356 if (!Util.DD) { 32357 YAHOO.log("DD dependency not met.", "error"); 32358 this.cfg.setProperty("draggable", false); 32359 return; 32360 } 32361 32362 if (this.header) { 32363 Dom.setStyle(this.header, "cursor", "move"); 32364 this.registerDragDrop(); 32365 } 32366 32367 this.subscribe("beforeShow", setWidthToOffsetWidth); 32368 32369 } else { 32370 32371 if (this.dd) { 32372 this.dd.unreg(); 32373 } 32374 32375 if (this.header) { 32376 Dom.setStyle(this.header,"cursor","auto"); 32377 } 32378 32379 this.unsubscribe("beforeShow", setWidthToOffsetWidth); 32380 } 32381 }, 32382 32383 /** 32384 * The default event handler fired when the "underlay" property 32385 * is changed. 32386 * @method configUnderlay 32387 * @param {String} type The CustomEvent type (usually the property name) 32388 * @param {Object[]} args The CustomEvent arguments. For configuration 32389 * handlers, args[0] will equal the newly applied value for the property. 32390 * @param {Object} obj The scope object. For configuration handlers, 32391 * this will usually equal the owner. 32392 */ 32393 configUnderlay: function (type, args, obj) { 32394 32395 var bMacGecko = (this.platform == "mac" && UA.gecko), 32396 sUnderlay = args[0].toLowerCase(), 32397 oUnderlay = this.underlay, 32398 oElement = this.element; 32399 32400 function createUnderlay() { 32401 var bNew = false;
32402 if (!oUnderlay) { // create if not already in DOM 32403 32404 if (!m_oUnderlayTemplate) { 32405 m_oUnderlayTemplate = document.createElement("div"); 32406 m_oUnderlayTemplate.className = "underlay"; 32407 } 32408 32409 oUnderlay = m_oUnderlayTemplate.cloneNode(false); 32410 this.element.appendChild(oUnderlay); 32411 32412 this.underlay = oUnderlay; 32413 32414 if (bIEQuirks) { 32415 this.sizeUnderlay(); 32416 this.cfg.subscribeToConfigEvent("width", this.sizeUnderlay); 32417 this.cfg.subscribeToConfigEvent("height", this.sizeUnderlay); 32418 32419 this.changeContentEvent.subscribe(this.sizeUnderlay); 32420 YAHOO.widget.Module.textResizeEvent.subscribe(this.sizeUnderlay, this, true); 32421 } 32422 32423 if (UA.webkit && UA.webkit < 420) { 32424 this.changeContentEvent.subscribe(this.forceUnderlayRedraw); 32425 } 32426 32427 bNew = true; 32428 } 32429 } 32430 32431 function onBeforeShow() { 32432 var bNew = createUnderlay.call(this); 32433 if (!bNew && bIEQuirks) { 32434 this.sizeUnderlay(); 32435 } 32436 this._underlayDeferred = false; 32437 this.beforeShowEvent.unsubscribe(onBeforeShow); 32438 } 32439 32440 function destroyUnderlay() { 32441 if (this._underlayDeferred) { 32442 this.beforeShowEvent.unsubscribe(onBeforeShow); 32443 this._underlayDeferred = false; 32444 } 32445 32446 if (oUnderlay) { 32447 this.cfg.unsubscribeFromConfigEvent("width", this.sizeUnderlay); 32448 this.cfg.unsubscribeFromConfigEvent("height",this.sizeUnderlay); 32449 this.changeContentEvent.unsubscribe(this.sizeUnderlay); 32450 this.changeContentEvent.unsubscribe(this.forceUnderlayRedraw); 32451 YAHOO.widget.Module.textResizeEvent.unsubscribe(this.sizeUnderlay, this, true); 32452 32453 this.element.removeChild(oUnderlay); 32454 32455 this.underlay = null; 32456 } 32457 } 32458 32459 switch (sUnderlay) { 32460 case "shadow": 32461 Dom.removeClass(oElement, "matte"); 32462 Dom.addClass(oElement, "shadow"); 32463 break; 32464 case "matte": 32465 if (!bMacGecko) { 32466 destroyUnderlay.call(this); 32467 } 32468 Dom.removeClass(oElement, "shadow"); 32469 Dom.addClass(oElement, "matte"); 32470 break; 32471 default: 32472 if (!bMacGecko) { 32473 destroyUnderlay.call(this); 32474 } 32475 Dom.removeClass(oElement, "shadow"); 32476 Dom.removeClass(oElement, "matte"); 32477 break; 32478 } 32479 32480 if ((sUnderlay == "shadow") || (bMacGecko && !oUnderlay)) { 32481 if (this.cfg.getProperty("visible")) { 32482 var bNew = createUnderlay.call(this); 32483 if (!bNew && bIEQuirks) { 32484 this.sizeUnderlay(); 32485 } 32486 } else { 32487 if (!this._underlayDeferred) { 32488 this.beforeShowEvent.subscribe(onBeforeShow); 32489 this._underlayDeferred = true; 32490 } 32491 } 32492 } 32493 }, 32494 32495 /** 32496 * The default event handler fired when the "modal" property is 32497 * changed. This handler subscribes or unsubscribes to the show and hide 32498 * events to handle the display or hide of the modality mask. 32499 * @method configModal 32500 * @param {String} type The CustomEvent type (usually the property name) 32501 * @param {Object[]} args The CustomEvent arguments. For configuration 32502 * handlers, args[0] will equal the newly applied value for the property. 32503 * @param {Object} obj The scope object. For configuration handlers, 32504 * this will usually equal the owner. 32505 */ 32506 configModal: function (type, args, obj) { 32507 32508 var modal = args[0]; 32509 if (modal) { 32510 if (!this._hasModalityEventListeners) { 32511 32512 this.subscribe("beforeShow", this.buildMask); 32513 this.subscribe("beforeShow", this.bringToTop); 32514 this.subscribe("beforeShow", this.showMask); 32515 this.subscribe("hide", this.hideMask); 32516 32517 Overlay.windowResizeEvent.subscribe(this.sizeMask, 32518 this, true); 32519 32520 this._hasModalityEventListeners = true; 32521 } 32522 } else { 32523 if (this._hasModalityEventListeners) { 32524 32525 if (this.cfg.getProperty("visible")) { 32526 this.hideMask(); 32527 this.removeMask(); 32528 } 32529 32530 this.unsubscribe("beforeShow", this.buildMask); 32531 this.unsubscribe("beforeShow", this.bringToTop); 32532 this.unsubscribe("beforeShow", this.showMask); 32533 this.unsubscribe("hide", this.hideMask); 32534 32535 Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); 32536 32537 this._hasModalityEventListeners = false;
32538 } 32539 } 32540 }, 32541 32542 /** 32543 * Removes the modality mask. 32544 * @method removeMask 32545 */ 32546 removeMask: function () { 32547 32548 var oMask = this.mask, 32549 oParentNode; 32550 32551 if (oMask) { 32552 /* 32553 Hide the mask before destroying it to ensure that DOM 32554 event handlers on focusable elements get removed. 32555 */ 32556 this.hideMask(); 32557 32558 oParentNode = oMask.parentNode; 32559 if (oParentNode) { 32560 oParentNode.removeChild(oMask); 32561 } 32562 32563 this.mask = null; 32564 } 32565 }, 32566 32567 /** 32568 * The default event handler fired when the "keylisteners" property 32569 * is changed. 32570 * @method configKeyListeners 32571 * @param {String} type The CustomEvent type (usually the property name) 32572 * @param {Object[]} args The CustomEvent arguments. For configuration 32573 * handlers, args[0] will equal the newly applied value for the property. 32574 * @param {Object} obj The scope object. For configuration handlers, 32575 * this will usually equal the owner. 32576 */ 32577 configKeyListeners: function (type, args, obj) { 32578 32579 var listeners = args[0], 32580 listener, 32581 nListeners, 32582 i; 32583 32584 if (listeners) { 32585 32586 if (listeners instanceof Array) { 32587 32588 nListeners = listeners.length; 32589 32590 for (i = 0; i < nListeners; i++) { 32591 32592 listener = listeners[i]; 32593 32594 if (!Config.alreadySubscribed(this.showEvent, 32595 listener.enable, listener)) { 32596 32597 this.showEvent.subscribe(listener.enable, 32598 listener, true); 32599 32600 } 32601 32602 if (!Config.alreadySubscribed(this.hideEvent, 32603 listener.disable, listener)) { 32604 32605 this.hideEvent.subscribe(listener.disable, 32606 listener, true); 32607 32608 this.destroyEvent.subscribe(listener.disable, 32609 listener, true); 32610 } 32611 } 32612 32613 } else { 32614 32615 if (!Config.alreadySubscribed(this.showEvent, 32616 listeners.enable, listeners)) { 32617 32618 this.showEvent.subscribe(listeners.enable, 32619 listeners, true); 32620 } 32621 32622 if (!Config.alreadySubscribed(this.hideEvent, 32623 listeners.disable, listeners)) { 32624 32625 this.hideEvent.subscribe(listeners.disable, 32626 listeners, true); 32627 32628 this.destroyEvent.subscribe(listeners.disable, 32629 listeners, true); 32630 32631 } 32632 32633 } 32634 32635 } 32636 32637 }, 32638 32639 /** 32640 * The default handler for the "strings" property 32641 * @method configStrings 32642 */
32643 configStrings : function(type, args, obj) { 32644 var val = Lang.merge(DEFAULT_CONFIG.STRINGS.value, args[0]); 32645 this.cfg.setProperty(DEFAULT_CONFIG.STRINGS.key, val, true); 32646 }, 32647 32648 /** 32649 * The default event handler fired when the "height" property is changed. 32650 * @method configHeight 32651 * @param {String} type The CustomEvent type (usually the property name) 32652 * @param {Object[]} args The CustomEvent arguments. For configuration 32653 * handlers, args[0] will equal the newly applied value for the property. 32654 * @param {Object} obj The scope object. For configuration handlers, 32655 * this will usually equal the owner. 32656 */ 32657 configHeight: function (type, args, obj) { 32658 var height = args[0], 32659 el = this.innerElement; 32660 32661 Dom.setStyle(el, "height", height); 32662 this.cfg.refireEvent("iframe"); 32663 }, 32664 32665 /** 32666 * The default custom event handler executed when the Panel's height is changed, 32667 * if the autofillheight property has been set. 32668 * 32669 * @method _autoFillOnHeightChange 32670 * @protected 32671 * @param {String} type The event type 32672 * @param {Array} args The array of arguments passed to event subscribers 32673 * @param {HTMLElement} el The header, body or footer element which is to be resized to fill 32674 * out the containers height 32675 */ 32676 _autoFillOnHeightChange : function(type, args, el) { 32677 Panel.superclass._autoFillOnHeightChange.apply(this, arguments); 32678 if (bIEQuirks) { 32679 var panel = this; 32680 setTimeout(function() { 32681 panel.sizeUnderlay(); 32682 },0); 32683 } 32684 }, 32685 32686 /** 32687 * The default event handler fired when the "width" property is changed. 32688 * @method configWidth 32689 * @param {String} type The CustomEvent type (usually the property name) 32690 * @param {Object[]} args The CustomEvent arguments. For configuration 32691 * handlers, args[0] will equal the newly applied value for the property. 32692 * @param {Object} obj The scope object. For configuration handlers, 32693 * this will usually equal the owner. 32694 */ 32695 configWidth: function (type, args, obj) { 32696 32697 var width = args[0], 32698 el = this.innerElement; 32699 32700 Dom.setStyle(el, "width", width); 32701 this.cfg.refireEvent("iframe"); 32702 32703 }, 32704 32705 /** 32706 * The default event handler fired when the "zIndex" property is changed. 32707 * @method configzIndex 32708 * @param {String} type The CustomEvent type (usually the property name) 32709 * @param {Object[]} args The CustomEvent arguments. For configuration 32710 * handlers, args[0] will equal the newly applied value for the property. 32711 * @param {Object} obj The scope object. For configuration handlers, 32712 * this will usually equal the owner. 32713 */ 32714 configzIndex: function (type, args, obj) { 32715 Panel.superclass.configzIndex.call(this, type, args, obj); 32716 32717 if (this.mask || this.cfg.getProperty("modal") === true) { 32718 var panelZ = Dom.getStyle(this.element, "zIndex"); 32719 if (!panelZ || isNaN(panelZ)) { 32720 panelZ = 0; 32721 } 32722 32723 if (panelZ === 0) { 32724 // Recursive call to configzindex (which should be stopped 32725 // from going further because panelZ should no longer === 0) 32726 this.cfg.setProperty("zIndex", 1); 32727 } else { 32728 this.stackMask(); 32729 } 32730 } 32731 }, 32732 32733 // END BUILT-IN PROPERTY EVENT HANDLERS // 32734 /** 32735 * Builds the wrapping container around the Panel that is used for 32736 * positioning the shadow and matte underlays. The container element is 32737 * assigned to a local instance variable called container, and the 32738 * element is reinserted inside of it. 32739 * @method buildWrapper 32740 */ 32741 buildWrapper: function () { 32742 32743 var elementParent = this.element.parentNode, 32744 originalElement = this.element, 32745 wrapper = document.createElement("div"); 32746 32747 wrapper.className = Panel.CSS_PANEL_CONTAINER; 32748 wrapper.id = originalElement.id + "_c"; 32749 32750 if (elementParent) { 32751 elementParent.insertBefore(wrapper, originalElement); 32752 } 32753 32754 wrapper.appendChild(originalElement); 32755 32756 this.element = wrapper; 32757 this.innerElement = originalElement; 32758 32759 Dom.setStyle(this.innerElement, "visibility", "inherit"); 32760 }, 32761 32762 /** 32763 * Adjusts the size of the shadow based on the size of the element. 32764 * @method sizeUnderlay 32765 */ 32766 sizeUnderlay: function () { 32767 var oUnderlay = this.underlay, 32768 oElement; 32769 32770 if (oUnderlay) { 32771 oElement = this.element; 32772 oUnderlay.style.width = oElement.offsetWidth + "px"; 32773 oUnderlay.style.height = oElement.offsetHeight + "px"; 32774 } 32775 }, 32776 32777 /** 32778 * Registers the Panel's header for drag & drop capability. 32779 * @method registerDragDrop 32780 */ 32781 registerDragDrop: function () { 32782 32783 var me = this; 32784 32785 if (this.header) { 32786 32787 if (!Util.DD) { 32788 YAHOO.log("DD dependency not met.", "error"); 32789 return; 32790 } 32791 32792 var bDragOnly = (this.cfg.getProperty("dragonly") === true); 32793 32794 /** 32795 * The YAHOO.util.DD instance, used to implement the draggable header for the panel if draggable is enabled 32796 * 32797 * @property dd 32798 * @type YAHOO.util.DD 32799 */
32800 this.dd = new Util.DD(this.element.id, this.id, {dragOnly: bDragOnly}); 32801 32802 if (!this.header.id) { 32803 this.header.id = this.id + "_h"; 32804 } 32805 32806 this.dd.startDrag = function () { 32807 32808 var offsetHeight, 32809 offsetWidth, 32810 viewPortWidth, 32811 viewPortHeight, 32812 scrollX, 32813 scrollY; 32814 32815 if (YAHOO.env.ua.ie == 6) { 32816 Dom.addClass(me.element,"drag"); 32817 } 32818 32819 if (me.cfg.getProperty("constraintoviewport")) { 32820 32821 var nViewportOffset = Overlay.VIEWPORT_OFFSET; 32822 32823 offsetHeight = me.element.offsetHeight; 32824 offsetWidth = me.element.offsetWidth; 32825 32826 viewPortWidth = Dom.getViewportWidth(); 32827 viewPortHeight = Dom.getViewportHeight(); 32828 32829 scrollX = Dom.getDocumentScrollLeft(); 32830 scrollY = Dom.getDocumentScrollTop(); 32831 32832 if (offsetHeight + nViewportOffset < viewPortHeight) { 32833 this.minY = scrollY + nViewportOffset; 32834 this.maxY = scrollY + viewPortHeight - offsetHeight - nViewportOffset; 32835 } else { 32836 this.minY = scrollY + nViewportOffset; 32837 this.maxY = scrollY + nViewportOffset; 32838 } 32839 32840 if (offsetWidth + nViewportOffset < viewPortWidth) { 32841 this.minX = scrollX + nViewportOffset; 32842 this.maxX = scrollX + viewPortWidth - offsetWidth - nViewportOffset; 32843 } else { 32844 this.minX = scrollX + nViewportOffset; 32845 this.maxX = scrollX + nViewportOffset; 32846 } 32847 32848 this.constrainX = true; 32849 this.constrainY = true; 32850 } else { 32851 this.constrainX = false; 32852 this.constrainY = false; 32853 } 32854 32855 me.dragEvent.fire("startDrag", arguments); 32856 }; 32857 32858 this.dd.onDrag = function () { 32859 me.syncPosition(); 32860 me.cfg.refireEvent("iframe"); 32861 if (this.platform == "mac" && YAHOO.env.ua.gecko) { 32862 this.showMacGeckoScrollbars(); 32863 } 32864 32865 me.dragEvent.fire("onDrag", arguments); 32866 }; 32867 32868 this.dd.endDrag = function () { 32869 32870 if (YAHOO.env.ua.ie == 6) { 32871 Dom.removeClass(me.element,"drag"); 32872 } 32873 32874 me.dragEvent.fire("endDrag", arguments); 32875 me.moveEvent.fire(me.cfg.getProperty("xy")); 32876 32877 }; 32878 32879 this.dd.setHandleElId(this.header.id); 32880 this.dd.addInvalidHandleType("INPUT"); 32881 this.dd.addInvalidHandleType("SELECT"); 32882 this.dd.addInvalidHandleType("TEXTAREA"); 32883 } 32884 }, 32885 32886 /** 32887 * Builds the mask that is laid over the document when the Panel is 32888 * configured to be modal. 32889 * @method buildMask 32890 */ 32891 buildMask: function () { 32892 var oMask = this.mask; 32893 if (!oMask) { 32894 if (!m_oMaskTemplate) { 32895 m_oMaskTemplate = document.createElement("div"); 32896 m_oMaskTemplate.className = "mask"; 32897 m_oMaskTemplate.innerHTML = " "; 32898 } 32899 oMask = m_oMaskTemplate.cloneNode(true); 32900 oMask.id = this.id + "_mask"; 32901 32902 document.body.insertBefore(oMask, document.body.firstChild); 32903 32904 this.mask = oMask; 32905 32906 if (YAHOO.env.ua.gecko && this.platform == "mac") { 32907 Dom.addClass(this.mask, "block-scrollbars"); 32908 } 32909 32910 // Stack mask based on the element zindex 32911 this.stackMask(); 32912 } 32913 }, 32914 32915 /** 32916 * Hides the modality mask. 32917 * @method hideMask 32918 */ 32919 hideMask: function () { 32920 if (this.cfg.getProperty("modal") && this.mask && this.beforeHideMaskEvent.fire()) { 32921 this.mask.style.display = "none"; 32922 Dom.removeClass(document.body, "masked"); 32923 this.hideMaskEvent.fire(); 32924 } 32925 }, 32926 32927 /** 32928 * Shows the modality mask. 32929 * @method showMask 32930 */ 32931 showMask: function () { 32932 if (this.cfg.getProperty("modal") && this.mask && this.beforeShowMaskEvent.fire()) { 32933 Dom.addClass(document.body, "masked"); 32934 this.sizeMask(); 32935 this.mask.style.display = "block"; 32936 this.showMaskEvent.fire(); 32937 } 32938 }, 32939 32940 /** 32941 * Sets the size of the modality mask to cover the entire scrollable 32942 * area of the document 32943 * @method sizeMask 32944 */ 32945 sizeMask: function () { 32946 if (this.mask) { 32947 32948 // Shrink mask first, so it doesn't affect the document size. 32949 var mask = this.mask, 32950 viewWidth = Dom.getViewportWidth(),
32951 viewHeight = Dom.getViewportHeight(); 32952 32953 if (mask.offsetHeight > viewHeight) { 32954 mask.style.height = viewHeight + "px"; 32955 } 32956 32957 if (mask.offsetWidth > viewWidth) { 32958 mask.style.width = viewWidth + "px"; 32959 } 32960 32961 // Then size it to the document 32962 mask.style.height = Dom.getDocumentHeight() + "px"; 32963 mask.style.width = Dom.getDocumentWidth() + "px"; 32964 } 32965 }, 32966 32967 /** 32968 * Sets the zindex of the mask, if it exists, based on the zindex of 32969 * the Panel element. The zindex of the mask is set to be one less 32970 * than the Panel element's zindex. 32971 * 32972 * <p>NOTE: This method will not bump up the zindex of the Panel 32973 * to ensure that the mask has a non-negative zindex. If you require the 32974 * mask zindex to be 0 or higher, the zindex of the Panel 32975 * should be set to a value higher than 0, before this method is called. 32976 * </p> 32977 * @method stackMask 32978 */ 32979 stackMask: function() { 32980 if (this.mask) { 32981 var panelZ = Dom.getStyle(this.element, "zIndex"); 32982 if (!YAHOO.lang.isUndefined(panelZ) && !isNaN(panelZ)) { 32983 Dom.setStyle(this.mask, "zIndex", panelZ - 1); 32984 } 32985 } 32986 }, 32987 32988 /** 32989 * Renders the Panel by inserting the elements that are not already in 32990 * the main Panel into their correct places. Optionally appends the 32991 * Panel to the specified node prior to the render's execution. NOTE: 32992 * For Panels without existing markup, the appendToNode argument is 32993 * REQUIRED. If this argument is ommitted and the current element is 32994 * not present in the document, the function will return false, 32995 * indicating that the render was a failure. 32996 * @method render 32997 * @param {String} appendToNode The element id to which the Module 32998 * should be appended to prior to rendering <em>OR</em> 32999 * @param {HTMLElement} appendToNode The element to which the Module 33000 * should be appended to prior to rendering 33001 * @return {boolean} Success or failure of the render 33002 */ 33003 render: function (appendToNode) { 33004 return Panel.superclass.render.call(this, appendToNode, this.innerElement); 33005 }, 33006 33007 /** 33008 * Renders the currently set header into it's proper position under the 33009 * module element. If the module element is not provided, "this.innerElement" 33010 * is used. 33011 * 33012 * @method _renderHeader 33013 * @protected 33014 * @param {HTMLElement} moduleElement Optional. A reference to the module element 33015 */ 33016 _renderHeader: function(moduleElement){ 33017 moduleElement = moduleElement || this.innerElement; 33018 Panel.superclass._renderHeader.call(this, moduleElement); 33019 }, 33020 33021 /** 33022 * Renders the currently set body into it's proper position under the 33023 * module element. If the module element is not provided, "this.innerElement" 33024 * is used. 33025 * 33026 * @method _renderBody 33027 * @protected 33028 * @param {HTMLElement} moduleElement Optional. A reference to the module element. 33029 */ 33030 _renderBody: function(moduleElement){ 33031 moduleElement = moduleElement || this.innerElement; 33032 Panel.superclass._renderBody.call(this, moduleElement); 33033 }, 33034 33035 /** 33036 * Renders the currently set footer into it's proper position under the 33037 * module element. If the module element is not provided, "this.innerElement" 33038 * is used. 33039 * 33040 * @method _renderFooter 33041 * @protected 33042 * @param {HTMLElement} moduleElement Optional. A reference to the module element 33043 */ 33044 _renderFooter: function(moduleElement){ 33045 moduleElement = moduleElement || this.innerElement; 33046 Panel.superclass._renderFooter.call(this, moduleElement); 33047 }, 33048 33049 /** 33050 * Removes the Panel element from the DOM and sets all child elements 33051 * to null. 33052 * @method destroy 33053 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 33054 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 33055 */ 33056 destroy: function (shallowPurge) { 33057 Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); 33058 this.removeMask(); 33059 if (this.close) { 33060 Event.purgeElement(this.close); 33061 } 33062 Panel.superclass.destroy.call(this, shallowPurge); 33063 }, 33064 33065 /** 33066 * Forces the underlay element to be repainted through the application/removal
33067 * of a yui-force-redraw class to the underlay element. 33068 * 33069 * @method forceUnderlayRedraw 33070 */ 33071 forceUnderlayRedraw : function () { 33072 var u = this.underlay; 33073 Dom.addClass(u, "yui-force-redraw"); 33074 setTimeout(function(){Dom.removeClass(u, "yui-force-redraw");}, 0); 33075 }, 33076 33077 /** 33078 * Returns a String representation of the object. 33079 * @method toString 33080 * @return {String} The string representation of the Panel. 33081 */ 33082 toString: function () { 33083 return "Panel " + this.id; 33084 } 33085 33086 }); 33087 33088}()); 33089(function () { 33090 33091 /** 33092 * <p> 33093 * Dialog is an implementation of Panel that can be used to submit form 33094 * data. 33095 * </p> 33096 * <p> 33097 * Built-in functionality for buttons with event handlers is included. 33098 * If the optional YUI Button dependancy is included on the page, the buttons 33099 * created will be instances of YAHOO.widget.Button, otherwise regular HTML buttons 33100 * will be created. 33101 * </p> 33102 * <p> 33103 * Forms can be processed in 3 ways -- via an asynchronous Connection utility call, 33104 * a simple form POST or GET, or manually. The YUI Connection utility should be 33105 * included if you're using the default "async" postmethod, but is not required if 33106 * you're using any of the other postmethod values. 33107 * </p> 33108 * @namespace YAHOO.widget 33109 * @class Dialog 33110 * @extends YAHOO.widget.Panel 33111 * @constructor 33112 * @param {String} el The element ID representing the Dialog <em>OR</em> 33113 * @param {HTMLElement} el The element representing the Dialog 33114 * @param {Object} userConfig The configuration object literal containing 33115 * the configuration that should be set for this Dialog. See configuration 33116 * documentation for more details. 33117 */ 33118 YAHOO.widget.Dialog = function (el, userConfig) { 33119 YAHOO.widget.Dialog.superclass.constructor.call(this, el, userConfig); 33120 }; 33121 33122 var Event = YAHOO.util.Event, 33123 CustomEvent = YAHOO.util.CustomEvent, 33124 Dom = YAHOO.util.Dom, 33125 Dialog = YAHOO.widget.Dialog, 33126 Lang = YAHOO.lang, 33127 33128 /** 33129 * Constant representing the name of the Dialog's events 33130 * @property EVENT_TYPES 33131 * @private 33132 * @final 33133 * @type Object 33134 */ 33135 EVENT_TYPES = { 33136 "BEFORE_SUBMIT": "beforeSubmit", 33137 "SUBMIT": "submit", 33138 "MANUAL_SUBMIT": "manualSubmit", 33139 "ASYNC_SUBMIT": "asyncSubmit", 33140 "FORM_SUBMIT": "formSubmit", 33141 "CANCEL": "cancel" 33142 }, 33143 33144 /** 33145 * Constant representing the Dialog's configuration properties 33146 * @property DEFAULT_CONFIG 33147 * @private 33148 * @final 33149 * @type Object 33150 */ 33151 DEFAULT_CONFIG = { 33152 33153 "POST_METHOD": { 33154 key: "postmethod", 33155 value: "async" 33156 }, 33157 33158 "POST_DATA" : { 33159 key: "postdata", 33160 value: null 33161 }, 33162 33163 "BUTTONS": { 33164 key: "buttons", 33165 value: "none", 33166 supercedes: ["visible"] 33167 }, 33168 33169 "HIDEAFTERSUBMIT" : { 33170 key: "hideaftersubmit", 33171 value: true 33172 } 33173 33174 }; 33175 33176 /** 33177 * Constant representing the default CSS class used for a Dialog 33178 * @property YAHOO.widget.Dialog.CSS_DIALOG 33179 * @static 33180 * @final
33181 * @type String 33182 */ 33183 Dialog.CSS_DIALOG = "yui-dialog"; 33184 33185 function removeButtonEventHandlers() { 33186 33187 var aButtons = this._aButtons, 33188 nButtons, 33189 oButton, 33190 i; 33191 33192 if (Lang.isArray(aButtons)) { 33193 nButtons = aButtons.length; 33194 33195 if (nButtons > 0) { 33196 i = nButtons - 1; 33197 do { 33198 oButton = aButtons[i]; 33199 33200 if (YAHOO.widget.Button && oButton instanceof YAHOO.widget.Button) { 33201 oButton.destroy(); 33202 } 33203 else if (oButton.tagName.toUpperCase() == "BUTTON") { 33204 Event.purgeElement(oButton); 33205 Event.purgeElement(oButton, false); 33206 } 33207 } 33208 while (i--); 33209 } 33210 } 33211 } 33212 33213 YAHOO.extend(Dialog, YAHOO.widget.Panel, { 33214 33215 /** 33216 * @property form 33217 * @description Object reference to the Dialog's 33218 * <code><form></code> element. 33219 * @default null 33220 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 33221 * level-one-html.html#ID-40002357">HTMLFormElement</a> 33222 */ 33223 form: null, 33224 33225 /** 33226 * Initializes the class's configurable properties which can be changed 33227 * using the Dialog's Config object (cfg). 33228 * @method initDefaultConfig 33229 */ 33230 initDefaultConfig: function () { 33231 Dialog.superclass.initDefaultConfig.call(this); 33232 33233 /** 33234 * The internally maintained callback object for use with the 33235 * Connection utility. The format of the callback object is 33236 * similar to Connection Manager's callback object and is 33237 * simply passed through to Connection Manager when the async 33238 * request is made. 33239 * @property callback 33240 * @type Object 33241 */ 33242 this.callback = { 33243 33244 /** 33245 * The function to execute upon success of the 33246 * Connection submission (when the form does not 33247 * contain a file input element). 33248 * 33249 * @property callback.success 33250 * @type Function 33251 */ 33252 success: null, 33253 33254 /** 33255 * The function to execute upon failure of the 33256 * Connection submission 33257 * @property callback.failure 33258 * @type Function 33259 */ 33260 failure: null, 33261 33262 /** 33263 *<p> 33264 * The function to execute upon success of the 33265 * Connection submission, when the form contains 33266 * a file input element. 33267 * </p> 33268 * <p> 33269 * <em>NOTE:</em> Connection manager will not 33270 * invoke the success or failure handlers for the file 33271 * upload use case. This will be the only callback 33272 * handler invoked. 33273 * </p> 33274 * <p> 33275 * For more information, see the <a href="http://developer.yahoo.com/yui/connection/#file"> 33276 * Connection Manager documenation on file uploads</a>. 33277 * </p> 33278 * @property callback.upload 33279 * @type Function 33280 */ 33281 33282 /** 33283 * The arbitrary argument or arguments to pass to the Connection 33284 * callback functions 33285 * @property callback.argument 33286 * @type Object 33287 */ 33288 argument: null 33289 33290 }; 33291 33292 // Add form dialog config properties // 33293 /** 33294 * The method to use for posting the Dialog's form. Possible values 33295 * are "async", "form", and "manual". 33296 * @config postmethod
33297 * @type String 33298 * @default async 33299 */ 33300 this.cfg.addProperty(DEFAULT_CONFIG.POST_METHOD.key, { 33301 handler: this.configPostMethod, 33302 value: DEFAULT_CONFIG.POST_METHOD.value, 33303 validator: function (val) { 33304 if (val != "form" && val != "async" && val != "none" && 33305 val != "manual") { 33306 return false; 33307 } else { 33308 return true; 33309 } 33310 } 33311 }); 33312 33313 /** 33314 * Any additional post data which needs to be sent when using the 33315 * <a href="#config_postmethod">async</a> postmethod for dialog POST submissions. 33316 * The format for the post data string is defined by Connection Manager's 33317 * <a href="YAHOO.util.Connect.html#method_asyncRequest">asyncRequest</a> 33318 * method. 33319 * @config postdata 33320 * @type String 33321 * @default null 33322 */ 33323 this.cfg.addProperty(DEFAULT_CONFIG.POST_DATA.key, { 33324 value: DEFAULT_CONFIG.POST_DATA.value 33325 }); 33326 33327 /** 33328 * This property is used to configure whether or not the 33329 * dialog should be automatically hidden after submit. 33330 * 33331 * @config hideaftersubmit 33332 * @type Boolean 33333 * @default true 33334 */ 33335 this.cfg.addProperty(DEFAULT_CONFIG.HIDEAFTERSUBMIT.key, { 33336 value: DEFAULT_CONFIG.HIDEAFTERSUBMIT.value 33337 }); 33338 33339 /** 33340 * Array of object literals, each containing a set of properties 33341 * defining a button to be appended into the Dialog's footer. 33342 * 33343 * <p>Each button object in the buttons array can have three properties:</p> 33344 * <dl> 33345 * <dt>text:</dt> 33346 * <dd> 33347 * The text that will display on the face of the button. The text can 33348 * include HTML, as long as it is compliant with HTML Button specifications. The text is added to the DOM as HTML, 33349 * and should be escaped by the implementor if coming from an external source. 33350 * </dd> 33351 * <dt>handler:</dt> 33352 * <dd>Can be either: 33353 * <ol> 33354 * <li>A reference to a function that should fire when the 33355 * button is clicked. (In this case scope of this function is 33356 * always its Dialog instance.)</li> 33357 * 33358 * <li>An object literal representing the code to be 33359 * executed when the button is clicked. 33360 * 33361 * <p>Format:</p> 33362 * 33363 * <p> 33364 * <code>{ 33365 * <br> 33366 * <strong>fn:</strong> Function, // 33367 * The handler to call when the event fires. 33368 * <br> 33369 * <strong>obj:</strong> Object, // 33370 * An object to pass back to the handler. 33371 * <br> 33372 * <strong>scope:</strong> Object // 33373 * The object to use for the scope of the handler. 33374 * <br> 33375 * }</code> 33376 * </p> 33377 * </li> 33378 * </ol> 33379 * </dd> 33380 * <dt>isDefault:</dt> 33381 * <dd> 33382 * An optional boolean value that specifies that a button 33383 * should be highlighted and focused by default. 33384 * </dd> 33385 * </dl> 33386 * 33387 * <em>NOTE:</em>If the YUI Button Widget is included on the page, 33388 * the buttons created will be instances of YAHOO.widget.Button. 33389 * Otherwise, HTML Buttons (<code><BUTTON></code>) will be 33390 * created. 33391 * 33392 * @config buttons 33393 * @type {Array|String} 33394 * @default "none" 33395 */ 33396 this.cfg.addProperty(DEFAULT_CONFIG.BUTTONS.key, { 33397 handler: this.configButtons, 33398 value: DEFAULT_CONFIG.BUTTONS.value, 33399 supercedes : DEFAULT_CONFIG.BUTTONS.supercedes 33400 }); 33401 33402 }, 33403 33404 /** 33405 * Initializes the custom events for Dialog which are fired 33406 * automatically at appropriate times by the Dialog class. 33407 * @method initEvents 33408 */ 33409 initEvents: function () { 33410 Dialog.superclass.initEvents.call(this); 33411 33412 var SIGNATURE = CustomEvent.LIST; 33413 33414 /** 33415 * CustomEvent fired prior to submission 33416 * @event beforeSubmitEvent 33417 */ 33418 this.beforeSubmitEvent = 33419 this.createEvent(EVENT_TYPES.BEFORE_SUBMIT); 33420 this.beforeSubmitEvent.signature = SIGNATURE; 33421 33422 /** 33423 * CustomEvent fired after submission 33424 * @event submitEvent 33425 */
33426 this.submitEvent = this.createEvent(EVENT_TYPES.SUBMIT); 33427 this.submitEvent.signature = SIGNATURE; 33428 33429 /** 33430 * CustomEvent fired for manual submission, before the generic submit event is fired 33431 * @event manualSubmitEvent 33432 */ 33433 this.manualSubmitEvent = 33434 this.createEvent(EVENT_TYPES.MANUAL_SUBMIT); 33435 this.manualSubmitEvent.signature = SIGNATURE; 33436 33437 /** 33438 * CustomEvent fired after asynchronous submission, before the generic submit event is fired 33439 * 33440 * @event asyncSubmitEvent 33441 * @param {Object} conn The connection object, returned by YAHOO.util.Connect.asyncRequest 33442 */ 33443 this.asyncSubmitEvent = this.createEvent(EVENT_TYPES.ASYNC_SUBMIT); 33444 this.asyncSubmitEvent.signature = SIGNATURE; 33445 33446 /** 33447 * CustomEvent fired after form-based submission, before the generic submit event is fired 33448 * @event formSubmitEvent 33449 */ 33450 this.formSubmitEvent = this.createEvent(EVENT_TYPES.FORM_SUBMIT); 33451 this.formSubmitEvent.signature = SIGNATURE; 33452 33453 /** 33454 * CustomEvent fired after cancel 33455 * @event cancelEvent 33456 */ 33457 this.cancelEvent = this.createEvent(EVENT_TYPES.CANCEL); 33458 this.cancelEvent.signature = SIGNATURE; 33459 33460 }, 33461 33462 /** 33463 * The Dialog initialization method, which is executed for Dialog and 33464 * all of its subclasses. This method is automatically called by the 33465 * constructor, and sets up all DOM references for pre-existing markup, 33466 * and creates required markup if it is not already present. 33467 * 33468 * @method init 33469 * @param {String} el The element ID representing the Dialog <em>OR</em> 33470 * @param {HTMLElement} el The element representing the Dialog 33471 * @param {Object} userConfig The configuration object literal 33472 * containing the configuration that should be set for this Dialog. 33473 * See configuration documentation for more details. 33474 */ 33475 init: function (el, userConfig) { 33476 33477 /* 33478 Note that we don't pass the user config in here yet because 33479 we only want it executed once, at the lowest subclass level 33480 */ 33481 33482 Dialog.superclass.init.call(this, el/*, userConfig*/); 33483 33484 this.beforeInitEvent.fire(Dialog); 33485 33486 Dom.addClass(this.element, Dialog.CSS_DIALOG); 33487 33488 this.cfg.setProperty("visible", false); 33489 33490 if (userConfig) { 33491 this.cfg.applyConfig(userConfig, true); 33492 } 33493 33494 //this.showEvent.subscribe(this.focusFirst, this, true); 33495 this.beforeHideEvent.subscribe(this.blurButtons, this, true); 33496 33497 this.subscribe("changeBody", this.registerForm); 33498 33499 this.initEvent.fire(Dialog); 33500 }, 33501 33502 /** 33503 * Submits the Dialog's form depending on the value of the 33504 * "postmethod" configuration property. <strong>Please note: 33505 * </strong> As of version 2.3 this method will automatically handle 33506 * asyncronous file uploads should the Dialog instance's form contain 33507 * <code><input type="file"></code> elements. If a Dialog 33508 * instance will be handling asyncronous file uploads, its 33509 * <code>callback</code> property will need to be setup with a 33510 * <code>upload</code> handler rather than the standard 33511 * <code>success</code> and, or <code>failure</code> handlers. For more 33512 * information, see the <a href="http://developer.yahoo.com/yui/ 33513 * connection/#file">Connection Manager documenation on file uploads</a>. 33514 * @method doSubmit 33515 */ 33516 doSubmit: function () { 33517 33518 var Connect = YAHOO.util.Connect, 33519 oForm = this.form, 33520 bUseFileUpload = false, 33521 bUseSecureFileUpload = false, 33522 aElements, 33523 nElements, 33524 i, 33525 formAttrs; 33526 33527 switch (this.cfg.getProperty("postmethod")) { 33528 33529 case "async": 33530 aElements = oForm.elements; 33531 nElements = aElements.length; 33532 33533 if (nElements > 0) { 33534 i = nElements - 1; 33535 do { 33536 if (aElements[i].type == "file") { 33537 bUseFileUpload = true; 33538 break; 33539 } 33540 } 33541 while(i--); 33542 } 33543 33544 if (bUseFileUpload && YAHOO.env.ua.ie && this.isSecure) { 33545 bUseSecureFileUpload = true; 33546 } 33547 33548 formAttrs = this._getFormAttributes(oForm); 33549
33550 Connect.setForm(oForm, bUseFileUpload, bUseSecureFileUpload); 33551 33552 var postData = this.cfg.getProperty("postdata"); 33553 var c = Connect.asyncRequest(formAttrs.method, formAttrs.action, this.callback, postData); 33554 33555 this.asyncSubmitEvent.fire(c); 33556 33557 break; 33558 33559 case "form": 33560 oForm.submit(); 33561 this.formSubmitEvent.fire(); 33562 break; 33563 33564 case "none": 33565 case "manual": 33566 this.manualSubmitEvent.fire(); 33567 break; 33568 } 33569 }, 33570 33571 /** 33572 * Retrieves important attributes (currently method and action) from 33573 * the form element, accounting for any elements which may have the same name 33574 * as the attributes. Defaults to "POST" and "" for method and action respectively 33575 * if the attribute cannot be retrieved. 33576 * 33577 * @method _getFormAttributes 33578 * @protected 33579 * @param {HTMLFormElement} oForm The HTML Form element from which to retrieve the attributes 33580 * @return {Object} Object literal, with method and action String properties. 33581 */ 33582 _getFormAttributes : function(oForm){ 33583 var attrs = { 33584 method : null, 33585 action : null 33586 }; 33587 33588 if (oForm) { 33589 if (oForm.getAttributeNode) { 33590 var action = oForm.getAttributeNode("action"); 33591 var method = oForm.getAttributeNode("method"); 33592 33593 if (action) { 33594 attrs.action = action.value; 33595 } 33596 33597 if (method) { 33598 attrs.method = method.value; 33599 } 33600 33601 } else { 33602 attrs.action = oForm.getAttribute("action"); 33603 attrs.method = oForm.getAttribute("method"); 33604 } 33605 } 33606 33607 attrs.method = (Lang.isString(attrs.method) ? attrs.method : "POST").toUpperCase(); 33608 attrs.action = Lang.isString(attrs.action) ? attrs.action : ""; 33609 33610 return attrs; 33611 }, 33612 33613 /** 33614 * Prepares the Dialog's internal FORM object, creating one if one is 33615 * not currently present. 33616 * @method registerForm 33617 */ 33618 registerForm: function() { 33619 33620 var form = this.element.getElementsByTagName("form")[0]; 33621 33622 if (this.form) { 33623 if (this.form == form && Dom.isAncestor(this.element, this.form)) { 33624 return; 33625 } else { 33626 Event.purgeElement(this.form); 33627 this.form = null; 33628 } 33629 } 33630 33631 if (!form) { 33632 form = document.createElement("form"); 33633 form.name = "frm_" + this.id; 33634 this.body.appendChild(form); 33635 } 33636 33637 if (form) { 33638 this.form = form; 33639 Event.on(form, "submit", this._submitHandler, this, true); 33640 } 33641 }, 33642 33643 /** 33644 * Internal handler for the form submit event 33645 * 33646 * @method _submitHandler 33647 * @protected 33648 * @param {DOMEvent} e The DOM Event object 33649 */ 33650 _submitHandler : function(e) { 33651 Event.stopEvent(e); 33652 this.submit(); 33653 this.form.blur(); 33654 }, 33655 33656 /** 33657 * Sets up a tab, shift-tab loop between the first and last elements 33658 * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener 33659 * instance properties, which are reset everytime this method is invoked. 33660 * 33661 * @method setTabLoop 33662 * @param {HTMLElement} firstElement 33663 * @param {HTMLElement} lastElement 33664 * 33665 */ 33666 setTabLoop : function(firstElement, lastElement) { 33667 33668 firstElement = firstElement || this.firstButton; 33669 lastElement = lastElement || this.lastButton; 33670 33671 Dialog.superclass.setTabLoop.call(this, firstElement, lastElement); 33672 }, 33673 33674 /** 33675 * Protected internal method for setTabLoop, which can be used by 33676 * subclasses to jump in and modify the arguments passed in if required. 33677 * 33678 * @method _setTabLoop 33679 * @param {HTMLElement} firstElement 33680 * @param {HTMLElement} lastElement 33681 * @protected 33682 */ 33683 _setTabLoop : function(firstElement, lastElement) { 33684 firstElement = firstElement || this.firstButton; 33685 lastElement = this.lastButton || lastElement; 33686 33687 this.setTabLoop(firstElement, lastElement); 33688 }, 33689 33690 /** 33691 * Configures instance properties, pointing to the 33692 * first and last focusable elements in the Dialog's form. 33693 * 33694 * @method setFirstLastFocusable 33695 */ 33696 setFirstLastFocusable : function() { 33697 33698 Dialog.superclass.setFirstLastFocusable.call(this); 33699 33700 var i, l, el, elements = this.focusableElements; 33701 33702 this.firstFormElement = null; 33703 this.lastFormElement = null; 33704 33705 if (this.form && elements && elements.length > 0) { 33706 l = elements.length; 33707 33708 for (i = 0; i < l; ++i) { 33709 el = elements[i]; 33710 if (this.form === el.form) { 33711 this.firstFormElement = el; 33712 break; 33713 } 33714 } 33715 33716 for (i = l-1; i >= 0; --i) { 33717 el = elements[i]; 33718 if (this.form === el.form) { 33719 this.lastFormElement = el; 33720 break; 33721 } 33722 } 33723 } 33724 }, 33725 33726 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 33727 /** 33728 * The default event handler fired when the "close" property is 33729 * changed. The method controls the appending or hiding of the close 33730 * icon at the top right of the Dialog. 33731 * @method configClose 33732 * @param {String} type The CustomEvent type (usually the property name) 33733 * @param {Object[]} args The CustomEvent arguments. For 33734 * configuration handlers, args[0] will equal the newly applied value 33735 * for the property. 33736 * @param {Object} obj The scope object. For configuration handlers, 33737 * this will usually equal the owner. 33738 */ 33739 configClose: function (type, args, obj) { 33740 Dialog.superclass.configClose.apply(this, arguments); 33741 }, 33742 33743 /** 33744 * Event handler for the close icon 33745 * 33746 * @method _doClose 33747 * @protected 33748 * 33749 * @param {DOMEvent} e 33750 */ 33751 _doClose : function(e) { 33752 Event.preventDefault(e); 33753 this.cancel(); 33754 }, 33755 33756 /** 33757 * The default event handler for the "buttons" configuration property 33758 * @method configButtons 33759 * @param {String} type The CustomEvent type (usually the property name) 33760 * @param {Object[]} args The CustomEvent arguments. For configuration 33761 * handlers, args[0] will equal the newly applied value for the property. 33762 * @param {Object} obj The scope object. For configuration handlers, 33763 * this will usually equal the owner. 33764 */ 33765 configButtons: function (type, args, obj) { 33766 33767 var Button = YAHOO.widget.Button, 33768 aButtons = args[0], 33769 oInnerElement = this.innerElement, 33770 oButton, 33771 oButtonEl, 33772 oYUIButton, 33773 nButtons, 33774 oSpan, 33775 oFooter, 33776 i; 33777 33778 removeButtonEventHandlers.call(this); 33779 33780 this._aButtons = null; 33781 33782 if (Lang.isArray(aButtons)) { 33783 33784 oSpan = document.createElement("span"); 33785 oSpan.className = "button-group"; 33786 nButtons = aButtons.length; 33787 33788 this._aButtons = []; 33789 this.defaultHtmlButton = null; 33790 33791 for (i = 0; i < nButtons; i++) { 33792 oButton = aButtons[i]; 33793 33794 if (Button) { 33795 oYUIButton = new Button({ label: oButton.text, type:oButton.t
33795ype }); 33796 oYUIButton.appendTo(oSpan); 33797 33798 oButtonEl = oYUIButton.get("element"); 33799 33800 if (oButton.isDefault) { 33801 oYUIButton.addClass("default"); 33802 this.defaultHtmlButton = oButtonEl; 33803 } 33804 33805 if (Lang.isFunction(oButton.handler)) { 33806 33807 oYUIButton.set("onclick", { 33808 fn: oButton.handler, 33809 obj: this, 33810 scope: this 33811 }); 33812 33813 } else if (Lang.isObject(oButton.handler) && Lang.isFunction(oButton.handler.fn)) { 33814 33815 oYUIButton.set("onclick", { 33816 fn: oButton.handler.fn, 33817 obj: ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), 33818 scope: (oButton.handler.scope || this) 33819 }); 33820 33821 } 33822 33823 this._aButtons[this._aButtons.length] = oYUIButton; 33824 33825 } else { 33826 33827 oButtonEl = document.createElement("button"); 33828 oButtonEl.setAttribute("type", "button"); 33829 33830 if (oButton.isDefault) { 33831 oButtonEl.className = "default"; 33832 this.defaultHtmlButton = oButtonEl; 33833 } 33834 33835 oButtonEl.innerHTML = oButton.text; 33836 33837 if (Lang.isFunction(oButton.handler)) { 33838 Event.on(oButtonEl, "click", oButton.handler, this, true); 33839 } else if (Lang.isObject(oButton.handler) && 33840 Lang.isFunction(oButton.handler.fn)) { 33841 33842 Event.on(oButtonEl, "click", 33843 oButton.handler.fn, 33844 ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), 33845 (oButton.handler.scope || this)); 33846 } 33847 33848 oSpan.appendChild(oButtonEl); 33849 this._aButtons[this._aButtons.length] = oButtonEl; 33850 } 33851 33852 oButton.htmlButton = oButtonEl; 33853 33854 if (i === 0) { 33855 this.firstButton = oButtonEl; 33856 } 33857 33858 if (i == (nButtons - 1)) { 33859 this.lastButton = oButtonEl; 33860 } 33861 } 33862 33863 this.setFooter(oSpan); 33864 33865 oFooter = this.footer; 33866 33867 if (Dom.inDocument(this.element) && !Dom.isAncestor(oInnerElement, oFooter)) { 33868 oInnerElement.appendChild(oFooter); 33869 } 33870 33871 this.buttonSpan = oSpan; 33872 33873 } else { // Do cleanup 33874 oSpan = this.buttonSpan; 33875 oFooter = this.footer; 33876 if (oSpan && oFooter) { 33877 oFooter.removeChild(oSpan); 33878 this.buttonSpan = null; 33879 this.firstButton = null; 33880 this.lastButton = null; 33881 this.defaultHtmlButton = null; 33882 } 33883 } 33884 33885 this.changeContentEvent.fire(); 33886 }, 33887 33888 /** 33889 * @method getButtons 33890 * @description Returns an array containing each of the Dialog's 33891 * buttons, by default an array of HTML <code><BUTTON></code> 33892 * elements. If the Dialog's buttons were created using the 33893 * YAHOO.widget.Button class (via the inclusion of the optional Button 33894 * dependency on the page), an array of YAHOO.widget.Button instances 33895 * is returned. 33896 * @return {Array} 33897 */ 33898 getButtons: function () { 33899 return this._aButtons || null; 33900 }, 33901 33902 /** 33903 * <p> 33904 * Sets focus to the first focusable element in the Dialog's form if found, 33905 * else, the default button if found, else the first button defined via the 33906 * "buttons" configuration property. 33907 * </p> 33908 * <p> 33909 * This method is invoked when the Dialog is made visible. 33910 * </p>
33911 * @method focusFirst 33912 * @return {Boolean} true, if focused. false if not 33913 */ 33914 focusFirst: function (type, args, obj) { 33915 33916 var el = this.firstFormElement, 33917 focused = false; 33918 33919 if (args && args[1]) { 33920 Event.stopEvent(args[1]); 33921 33922 // When tabbing here, use firstElement instead of firstFormElement 33923 if (args[0] === 9 && this.firstElement) { 33924 el = this.firstElement; 33925 } 33926 } 33927 33928 if (el) { 33929 try { 33930 el.focus(); 33931 focused = true; 33932 } catch(oException) { 33933 // Ignore 33934 } 33935 } else { 33936 if (this.defaultHtmlButton) { 33937 focused = this.focusDefaultButton(); 33938 } else { 33939 focused = this.focusFirstButton(); 33940 } 33941 } 33942 return focused; 33943 }, 33944 33945 /** 33946 * Sets focus to the last element in the Dialog's form or the last 33947 * button defined via the "buttons" configuration property. 33948 * @method focusLast 33949 * @return {Boolean} true, if focused. false if not 33950 */ 33951 focusLast: function (type, args, obj) { 33952 33953 var aButtons = this.cfg.getProperty("buttons"), 33954 el = this.lastFormElement, 33955 focused = false; 33956 33957 if (args && args[1]) { 33958 Event.stopEvent(args[1]); 33959 33960 // When tabbing here, use lastElement instead of lastFormElement 33961 if (args[0] === 9 && this.lastElement) { 33962 el = this.lastElement; 33963 } 33964 } 33965 33966 if (aButtons && Lang.isArray(aButtons)) { 33967 focused = this.focusLastButton(); 33968 } else { 33969 if (el) { 33970 try { 33971 el.focus(); 33972 focused = true; 33973 } catch(oException) { 33974 // Ignore 33975 } 33976 } 33977 } 33978 33979 return focused; 33980 }, 33981 33982 /** 33983 * Helper method to normalize button references. It either returns the 33984 * YUI Button instance for the given element if found, 33985 * or the passes back the HTMLElement reference if a corresponding YUI Button 33986 * reference is not found or YAHOO.widget.Button does not exist on the page. 33987 * 33988 * @method _getButton 33989 * @private 33990 * @param {HTMLElement} button 33991 * @return {YAHOO.widget.Button|HTMLElement} 33992 */ 33993 _getButton : function(button) { 33994 var Button = YAHOO.widget.Button; 33995 33996 // If we have an HTML button and YUI Button is on the page, 33997 // get the YUI Button reference if available. 33998 if (Button && button && button.nodeName && button.id) { 33999 button = Button.getButton(button.id) || button; 34000 } 34001 34002 return button; 34003 }, 34004 34005 /** 34006 * Sets the focus to the button that is designated as the default via 34007 * the "buttons" configuration property. By default, this method is 34008 * called when the Dialog is made visible. 34009 * @method focusDefaultButton 34010 * @return {Boolean} true if focused, false if not 34011 */ 34012 focusDefaultButton: function () { 34013 var button = this._getButton(this.defaultHtmlButton), 34014 focused = false; 34015 34016 if (button) { 34017 /* 34018 Place the call to the "focus" method inside a try/catch 34019 block to prevent IE from throwing JavaScript errors if 34020 the element is disabled or hidden. 34021 */ 34022 try { 34023 button.focus(); 34024 focused = true; 34025 } catch(oException) { 34026 } 34027 } 34028 return focused; 34029 }, 34030 34031 /** 34032 * Blurs all the buttons defined via the "buttons" 34033 * configuration property. 34034 * @method blurButtons 34035 */ 34036 blurButtons: function () { 34037 34038 var aButtons = this.cfg.getProperty("buttons"), 34039 nButtons, 34040 oButton, 34041 oElement, 34042 i; 34043 34044 if (aButtons && Lang.isArray(aButtons)) { 34045 nButtons = aButtons.length; 34046 if (nButtons > 0) { 34047 i = (nButtons - 1); 34048 do { 34049 oButton = aButtons[i]; 34050 if (oButton) { 34051 oElement = this._getButton(oButton.htmlButton); 34052 if (oElement) { 34053 /* 34054 Place the call to the "blur" method inside 34055 a try/catch block to prevent IE from 34056 throwing JavaScript errors if the element 34057 is disabled or hidden. 34058 */ 34059 try { 34060 oElement.blur(); 34061 } catch(oException) { 34062 // ignore 34063 } 34064 } 34065 } 34066 } while(i--); 34067 } 34068 } 34069 }, 34070 34071 /** 34072 * Sets the focus to the first button created via the "buttons" 34073 * configuration property.
34074 * @method focusFirstButton 34075 * @return {Boolean} true, if focused. false if not 34076 */ 34077 focusFirstButton: function () { 34078 34079 var aButtons = this.cfg.getProperty("buttons"), 34080 oButton, 34081 oElement, 34082 focused = false; 34083 34084 if (aButtons && Lang.isArray(aButtons)) { 34085 oButton = aButtons[0]; 34086 if (oButton) { 34087 oElement = this._getButton(oButton.htmlButton); 34088 if (oElement) { 34089 /* 34090 Place the call to the "focus" method inside a 34091 try/catch block to prevent IE from throwing 34092 JavaScript errors if the element is disabled 34093 or hidden. 34094 */ 34095 try { 34096 oElement.focus(); 34097 focused = true; 34098 } catch(oException) { 34099 // ignore 34100 } 34101 } 34102 } 34103 } 34104 34105 return focused; 34106 }, 34107 34108 /** 34109 * Sets the focus to the last button created via the "buttons" 34110 * configuration property. 34111 * @method focusLastButton 34112 * @return {Boolean} true, if focused. false if not 34113 */ 34114 focusLastButton: function () { 34115 34116 var aButtons = this.cfg.getProperty("buttons"), 34117 nButtons, 34118 oButton, 34119 oElement, 34120 focused = false; 34121 34122 if (aButtons && Lang.isArray(aButtons)) { 34123 nButtons = aButtons.length; 34124 if (nButtons > 0) { 34125 oButton = aButtons[(nButtons - 1)]; 34126 34127 if (oButton) { 34128 oElement = this._getButton(oButton.htmlButton); 34129 if (oElement) { 34130 /* 34131 Place the call to the "focus" method inside a 34132 try/catch block to prevent IE from throwing 34133 JavaScript errors if the element is disabled 34134 or hidden. 34135 */ 34136 34137 try { 34138 oElement.focus(); 34139 focused = true; 34140 } catch(oException) { 34141 // Ignore 34142 } 34143 } 34144 } 34145 } 34146 } 34147 34148 return focused; 34149 }, 34150 34151 /** 34152 * The default event handler for the "postmethod" configuration property 34153 * @method configPostMethod 34154 * @param {String} type The CustomEvent type (usually the property name) 34155 * @param {Object[]} args The CustomEvent arguments. For 34156 * configuration handlers, args[0] will equal the newly applied value 34157 * for the property. 34158 * @param {Object} obj The scope object. For configuration handlers, 34159 * this will usually equal the owner. 34160 */ 34161 configPostMethod: function (type, args, obj) { 34162 this.registerForm(); 34163 }, 34164 34165 // END BUILT-IN PROPERTY EVENT HANDLERS // 34166 34167 /** 34168 * Built-in function hook for writing a validation function that will 34169 * be checked for a "true" value prior to a submit. This function, as 34170 * implemented by default, always returns true, so it should be 34171 * overridden if validation is necessary. 34172 * @method validate 34173 */ 34174 validate: function () { 34175 return true; 34176 }, 34177 34178 /** 34179 * Executes a submit of the Dialog if validation 34180 * is successful. By default the Dialog is hidden 34181 * after submission, but you can set the "hideaftersubmit" 34182 * configuration property to false, to prevent the Dialog 34183 * from being hidden. 34184 * 34185 * @method submit 34186 */ 34187 submit: function () { 34188 if (this.validate()) { 34189 if (this.beforeSubmitEvent.fire()) { 34190 this.doSubmit(); 34191 this.submitEvent.fire(); 34192 34193 if (this.cfg.getProperty("hideaftersubmit")) { 34194 this.hide(); 34195 } 34196 34197 return true; 34198 } else { 34199 return false;
34200 } 34201 } else { 34202 return false; 34203 } 34204 }, 34205 34206 /** 34207 * Executes the cancel of the Dialog followed by a hide. 34208 * @method cancel 34209 */ 34210 cancel: function () { 34211 this.cancelEvent.fire(); 34212 this.hide(); 34213 }, 34214 34215 /** 34216 * Returns a JSON-compatible data structure representing the data 34217 * currently contained in the form. 34218 * @method getData 34219 * @return {Object} A JSON object reprsenting the data of the 34220 * current form. 34221 */ 34222 getData: function () { 34223 34224 var oForm = this.form, 34225 aElements, 34226 nTotalElements, 34227 oData, 34228 sName, 34229 oElement, 34230 nElements, 34231 sType, 34232 sTagName, 34233 aOptions, 34234 nOptions, 34235 aValues, 34236 oOption, 34237 oRadio, 34238 oCheckbox, 34239 valueAttr, 34240 i, 34241 n; 34242 34243 function isFormElement(p_oElement) { 34244 var sTag = p_oElement.tagName.toUpperCase(); 34245 return ((sTag == "INPUT" || sTag == "TEXTAREA" || 34246 sTag == "SELECT") && p_oElement.name == sName); 34247 } 34248 34249 if (oForm) { 34250 34251 aElements = oForm.elements; 34252 nTotalElements = aElements.length; 34253 oData = {}; 34254 34255 for (i = 0; i < nTotalElements; i++) { 34256 sName = aElements[i].name; 34257 34258 /* 34259 Using "Dom.getElementsBy" to safeguard user from JS 34260 errors that result from giving a form field (or set of 34261 fields) the same name as a native method of a form 34262 (like "submit") or a DOM collection (such as the "item" 34263 method). Originally tried accessing fields via the 34264 "namedItem" method of the "element" collection, but 34265 discovered that it won't return a collection of fields 34266 in Gecko. 34267 */ 34268 34269 oElement = Dom.getElementsBy(isFormElement, "*", oForm); 34270 nElements = oElement.length; 34271 34272 if (nElements > 0) { 34273 if (nElements == 1) { 34274 oElement = oElement[0]; 34275 34276 sType = oElement.type; 34277 sTagName = oElement.tagName.toUpperCase(); 34278 34279 switch (sTagName) { 34280 case "INPUT": 34281 if (sType == "checkbox") { 34282 oData[sName] = oElement.checked; 34283 } else if (sType != "radio") { 34284 oData[sName] = oElement.value; 34285 } 34286 break; 34287 34288 case "TEXTAREA": 34289 oData[sName] = oElement.value; 34290 break; 34291 34292 case "SELECT": 34293 aOptions = oElement.options; 34294 nOptions = aOptions.length; 34295 aValues = []; 34296 34297 for (n = 0; n < nOptions; n++) { 34298 oOption = aOptions[n]; 34299 if (oOption.selected) { 34300 valueAttr = oOption.attributes.value; 34301 aValues[aValues.length] = (valueAttr && valueAttr.specified) ? oOption.value : oOption.text; 34302 } 34303 } 34304 oData[sName] = aValues; 34305 break; 34306 } 34307 34308 } else { 34309 sType = oElement[0].type; 34310 switch (sType) { 34311 case "radio": 34312 for (n = 0; n < nElements; n++) { 34313 oRadio = oElement[n]; 34314 if (oRadio.checked) { 34315 oData[sName] = oRadio.value; 34316 break; 34317 } 34318 } 34319 break; 34320 34321 case "checkbox": 34322 aValues = []; 34323 for (n = 0; n < nElements; n++) { 34324 oCheckbox = oElement[n]; 34325 if (oCheckbox.checked) { 34326 aValues[aValues.length] = oCheckbox.value; 34327 } 34328 } 34329 oData[sName] = aValues; 34330 break; 34331 } 34332 } 34333 } 34334 } 34335 } 34336 34337 return oData; 34338 }, 34339 34340 /** 34341 * Removes the Panel element from the DOM and sets all child elements 34342 * to null. 34343 * @method destroy 34344 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 34345 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 34346 */ 34347 destroy: function (shallowPurge) { 34348 removeButtonEventHandlers.call(this); 34349 34350 this._aButtons = null; 34351 34352 var aForms = this.element.getElementsByTagName("form"), 34353 oForm; 34354 34355 if (aForms.length > 0) { 34356 oForm = aForms[0]; 34357 34358 if (oForm) { 34359 Event.purgeElement(oForm); 34360 if (oForm.parentNode) { 34361 oForm.parentNode.removeChild(oForm); 34362 } 34363 this.form = null; 34364 } 34365 } 34366 Dialog.superclass.destroy.call(this, shallowPurge); 34367 }, 34368 34369 /** 34370 * Returns a string representation of the object. 34371 * @method toString 34372 * @return {String} The string representation of the Dialog 34373 */ 34374 toString: function () { 34375 return "Dialog " + this.id; 34376 } 34377 34378 }); 34379 34380}()); 34381(function () { 34382 34383 /** 34384 * SimpleDialog is a simple implementation of Dialog that can be used to 34385 * submit a single value. Forms can be processed in 3 ways -- via an 34386 * asynchronous Connection utility call, a simple form POST or GET, 34387 * or manually. 34388 * @namespace YAHOO.widget 34389 * @class SimpleDialog 34390 * @extends YAHOO.widget.Dialog 34391 * @constructor 34392 * @param {String} el The element ID representing the SimpleDialog 34393 * <em>OR</em> 34394 * @param {HTMLElement} el The element representing the SimpleDialog 34395 * @param {Object} userConfig The configuration object literal containing 34396 * the configuration that should be set for this SimpleDialog. See 34397 * configuration documentation for more details. 34398 */ 34399 YAHOO.widget.SimpleDialog = function (el, userConfig) { 34400 34401 YAHOO.widget.SimpleDialog.superclass.constructor.call(this, 34402 el, userConfig); 34403 34404 }; 34405 34406 var Dom = YAHOO.util.Dom, 34407 SimpleDialog = YAHOO.widget.SimpleDialog, 34408 34409 /** 34410 * Constant representing the SimpleDialog's configuration properties 34411 * @property DEFAULT_CONFIG 34412 * @private 34413 * @final 34414 * @type Object 34415 */ 34416 DEFAULT_CONFIG = { 34417 34418 "ICON": { 34419 key: "icon", 34420 value: "none", 34421 suppressEvent: true 34422 }, 34423 34424 "TEXT": { 34425 key: "text", 34426 value: "", 34427 suppressEvent: true, 34428 supercedes: ["icon"] 34429 } 34430 34431 }; 34432 34433 /** 34434 * Constant for the standard network icon for a blocking action 34435 * @property YAHOO.widget.SimpleDialog.ICON_BLOCK 34436 * @static 34437 * @final
34438 * @type String 34439 */ 34440 SimpleDialog.ICON_BLOCK = "blckicon"; 34441 34442 /** 34443 * Constant for the standard network icon for alarm 34444 * @property YAHOO.widget.SimpleDialog.ICON_ALARM 34445 * @static 34446 * @final 34447 * @type String 34448 */ 34449 SimpleDialog.ICON_ALARM = "alrticon"; 34450 34451 /** 34452 * Constant for the standard network icon for help 34453 * @property YAHOO.widget.SimpleDialog.ICON_HELP 34454 * @static 34455 * @final 34456 * @type String 34457 */ 34458 SimpleDialog.ICON_HELP = "hlpicon"; 34459 34460 /** 34461 * Constant for the standard network icon for info 34462 * @property YAHOO.widget.SimpleDialog.ICON_INFO 34463 * @static 34464 * @final 34465 * @type String 34466 */ 34467 SimpleDialog.ICON_INFO = "infoicon"; 34468 34469 /** 34470 * Constant for the standard network icon for warn 34471 * @property YAHOO.widget.SimpleDialog.ICON_WARN 34472 * @static 34473 * @final 34474 * @type String 34475 */ 34476 SimpleDialog.ICON_WARN = "warnicon"; 34477 34478 /** 34479 * Constant for the standard network icon for a tip 34480 * @property YAHOO.widget.SimpleDialog.ICON_TIP 34481 * @static 34482 * @final
34483 * @type String 34484 */ 34485 SimpleDialog.ICON_TIP = "tipicon"; 34486 34487 /** 34488 * Constant representing the name of the CSS class applied to the element 34489 * created by the "icon" configuration property. 34490 * @property YAHOO.widget.SimpleDialog.ICON_CSS_CLASSNAME 34491 * @static 34492 * @final 34493 * @type String 34494 */ 34495 SimpleDialog.ICON_CSS_CLASSNAME = "yui-icon"; 34496 34497 /** 34498 * Constant representing the default CSS class used for a SimpleDialog 34499 * @property YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG 34500 * @static 34501 * @final 34502 * @type String 34503 */ 34504 SimpleDialog.CSS_SIMPLEDIALOG = "yui-simple-dialog"; 34505 34506 34507 YAHOO.extend(SimpleDialog, YAHOO.widget.Dialog, { 34508 34509 /** 34510 * Initializes the class's configurable properties which can be changed 34511 * using the SimpleDialog's Config object (cfg). 34512 * @method initDefaultConfig 34513 */ 34514 initDefaultConfig: function () { 34515 34516 SimpleDialog.superclass.initDefaultConfig.call(this); 34517 34518 // Add dialog config properties // 34519 34520 /** 34521 * Sets the informational icon for the SimpleDialog 34522 * @config icon
34523 * @type String 34524 * @default "none" 34525 */ 34526 this.cfg.addProperty(DEFAULT_CONFIG.ICON.key, { 34527 handler: this.configIcon, 34528 value: DEFAULT_CONFIG.ICON.value, 34529 suppressEvent: DEFAULT_CONFIG.ICON.suppressEvent 34530 }); 34531 34532 /** 34533 * Sets the text for the SimpleDialog. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 34534 * @config text 34535 * @type HTML 34536 * @default "" 34537 */ 34538 this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { 34539 handler: this.configText, 34540 value: DEFAULT_CONFIG.TEXT.value, 34541 suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent, 34542 supercedes: DEFAULT_CONFIG.TEXT.supercedes 34543 }); 34544 34545 }, 34546 34547 34548 /** 34549 * The SimpleDialog initialization method, which is executed for 34550 * SimpleDialog and all of its subclasses. This method is automatically 34551 * called by the constructor, and sets up all DOM references for 34552 * pre-existing markup, and creates required markup if it is not 34553 * already present. 34554 * @method init 34555 * @param {String} el The element ID representing the SimpleDialog 34556 * <em>OR</em> 34557 * @param {HTMLElement} el The element representing the SimpleDialog 34558 * @param {Object} userConfig The configuration object literal 34559 * containing the configuration that should be set for this 34560 * SimpleDialog. See configuration documentation for more details. 34561 */ 34562 init: function (el, userConfig) { 34563 34564 /* 34565 Note that we don't pass the user config in here yet because we 34566 only want it executed once, at the lowest subclass level 34567 */ 34568 34569 SimpleDialog.superclass.init.call(this, el/*, userConfig*/); 34570 34571 this.beforeInitEvent.fire(SimpleDialog); 34572 34573 Dom.addClass(this.element, SimpleDialog.CSS_SIMPLEDIALOG); 34574 34575 this.cfg.queueProperty("postmethod", "manual"); 34576 34577 if (userConfig) { 34578 this.cfg.applyConfig(userConfig, true); 34579 } 34580 34581 this.beforeRenderEvent.subscribe(function () { 34582 if (! this.body) { 34583 this.setBody(""); 34584 } 34585 }, this, true); 34586 34587 this.initEvent.fire(SimpleDialog); 34588 34589 }, 34590 34591 /** 34592 * Prepares the SimpleDialog's internal FORM object, creating one if one 34593 * is not currently present, and adding the value hidden field. 34594 * @method registerForm 34595 */ 34596 registerForm: function () { 34597 SimpleDialog.superclass.registerForm.call(this); 34598 34599 var doc = this.form.ownerDocument, 34600 input = doc.createElement("input"); 34601 34602 input.type = "hidden"; 34603 input.name = this.id; 34604 input.value = ""; 34605 34606 this.form.appendChild(input); 34607 }, 34608 34609 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 34610 34611 /** 34612 * Fired when the "icon" property is set. 34613 * @method configIcon 34614 * @param {String} type The CustomEvent type (usually the property name) 34615 * @param {Object[]} args The CustomEvent arguments. For configuration 34616 * handlers, args[0] will equal the newly applied value for the property. 34617 * @param {Object} obj The scope object. For configuration handlers, 34618 * this will usually equal the owner. 34619 */ 34620 configIcon: function (type,args,obj) { 34621 34622 var sIcon = args[0], 34623 oBody = this.body, 34624 sCSSClass = SimpleDialog.ICON_CSS_CLASSNAME, 34625 aElements, 34626 oIcon, 34627 oIconParent; 34628 34629 if (sIcon && sIcon != "none") { 34630 34631 aElements = Dom.getElementsByClassName(sCSSClass, "*" , oBody); 34632 34633 if (aElements.length === 1) { 34634 34635 oIcon = aElements[0]; 34636 oIconParent = oIcon.parentNode; 34637 34638 if (oIconParent) { 34639 34640 oIconParent.removeChild(oIcon); 34641 34642 oIcon = null; 34643 34644 } 34645 34646 } 34647 34648 34649 if (sIcon.indexOf(".") == -1) { 34650 34651 oIcon = document.createElement("span"); 34652 oIcon.className = (sCSSClass + " " + sIcon); 34653 oIcon.innerHTML = " "; 34654 34655 } else { 34656 34657 oIcon = document.createElement("img"); 34658 oIcon.src = (this.imageRoot + sIcon); 34659 oIcon.className = sCSSClass; 34660 34661 } 34662 34663 34664 if (oIcon) { 34665 34666 oBody.insertBefore(oIcon, oBody.firstChild); 34667 34668 } 34669 34670 } 34671 34672 }, 34673 34674 /** 34675 * Fired when the "text" property is set. 34676 * @method configText 34677 * @param {String} type The CustomEvent type (usually the property name) 34678 * @param {Object[]} args The CustomEvent arguments. For configuration 34679 * handlers, args[0] will equal the newly applied value for the property. 34680 * @param {Object} obj The scope object. For configuration handlers, 34681 * this will usually equal the owner. 34682 */ 34683 configText: function (type,args,obj) { 34684 var text = args[0]; 34685 if (text) { 34686 this.setBody(text); 34687 this.cfg.refireEvent("icon"); 34688 } 34689 }, 34690 34691 // END BUILT-IN PROPERTY EVENT HANDLERS // 34692 34693 /** 34694 * Returns a string representation of the object. 34695 * @method toString 34696 * @return {String} The string representation of the SimpleDialog 34697 */ 34698 toString: function () { 34699 return "SimpleDialog " + this.id; 34700 } 34701 34702 /** 34703 * <p> 34704 * Sets the SimpleDialog's body content to the HTML specified. 34705 * If no body is present, one will be automatically created. 34706 * An empty string can be passed to the method to clear the contents of the body. 34707 * </p> 34708 * <p><strong>NOTE:</strong> SimpleDialog provides the <a href="#config_text">text</a> 34709 * and <a href="#config_icon">icon</a> configuration properties to set the contents 34710 * of it's body element in accordance with the UI design for a SimpleDialog (an 34711 * icon and message text). Calling setBody on the SimpleDialog will not enforce this 34712 * UI design constraint and will replace the entire contents of the SimpleDialog body. 34713 * It should only be used if you wish the replace the default icon/text body structure 34714 * of a SimpleDialog with your own custom markup.</p> 34715 * 34716 * @method setBody 34717 * @param {HTML} bodyContent The HTML used to set the body. 34718 * As a convenience, non HTMLElement objects can also be passed into 34719 * the method, and will be treated as strings, with the body innerHTML 34720 * set to their default toString implementations. 34721 * 34722 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 34723 * 34724 * <em>OR</em> 34725 * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only child of the body element. 34726 * <em>OR</em> 34727 * @param {DocumentFragment} bodyContent The document fragment 34728 * containing elements which are to be added to the body 34729 */ 34730 }); 34731 34732}()); 34733(function () { 34734 34735 /** 34736 * ContainerEffect encapsulates animation transitions that are executed when 34737 * an Overlay is shown or hidden. 34738 * @namespace YAHOO.widget 34739 * @class ContainerEffect 34740 * @constructor 34741 * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation 34742 * should be associated with 34743 * @param {Object} attrIn The object literal representing the animation 34744 * arguments to be used for the animate-in transition. The arguments for 34745 * this literal are: attributes(object, see YAHOO.util.Anim for description), 34746 * duration(Number), and method(i.e. Easing.easeIn). 34747 * @param {Object} attrOut The object literal representing the animation 34748 * arguments to be used for the animate-out transition. The arguments for 34749 * this literal are: attributes(object, see YAHOO.util.Anim for description), 34750 * duration(Number), and method(i.e. Easing.easeIn). 34751 * @param {HTMLElement} targetElement Optional. The target element that 34752 * should be animated during the transition. Defaults to overlay.element. 34753 * @param {class} Optional. The animation class to instantiate. Defaults to 34754 * YAHOO.util.Anim. Other options include YAHOO.util.Motion. 34755 */ 34756 YAHOO.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) { 34757 34758 if (!animClass) { 34759 animClass = YAHOO.util.Anim; 34760 } 34761 34762 /** 34763 * The overlay to animate 34764 * @property overlay 34765 * @type YAHOO.widget.Overlay 34766 */
34767 this.overlay = overlay; 34768 34769 /** 34770 * The animation attributes to use when transitioning into view 34771 * @property attrIn 34772 * @type Object 34773 */ 34774 this.attrIn = attrIn; 34775 34776 /** 34777 * The animation attributes to use when transitioning out of view 34778 * @property attrOut 34779 * @type Object 34780 */ 34781 this.attrOut = attrOut; 34782 34783 /** 34784 * The target element to be animated 34785 * @property targetElement 34786 * @type HTMLElement 34787 */ 34788 this.targetElement = targetElement || overlay.element; 34789 34790 /** 34791 * The animation class to use for animating the overlay 34792 * @property animClass 34793 * @type class 34794 */ 34795 this.animClass = animClass; 34796 }; 34797 34798 var Dom = YAHOO.util.Dom, 34799 CustomEvent = YAHOO.util.CustomEvent, 34800 ContainerEffect = YAHOO.widget.ContainerEffect; 34801 34802 /** 34803 * A pre-configured ContainerEffect instance that can be used for fading 34804 * an overlay in and out. 34805 * @method FADE 34806 * @static 34807 * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate 34808 * @param {Number} dur The duration of the animation 34809 * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object 34810 */ 34811 ContainerEffect.FADE = function (overlay, dur) { 34812 34813 var Easing = YAHOO.util.Easing, 34814 fin = { 34815 attributes: {opacity:{from:0, to:1}}, 34816 duration: dur, 34817 method: Easing.easeIn 34818 }, 34819 fout = { 34820 attributes: {opacity:{to:0}}, 34821 duration: dur, 34822 method: Easing.easeOut 34823 }, 34824 fade = new ContainerEffect(overlay, fin, fout, overlay.element); 34825 34826 fade.handleUnderlayStart = function() { 34827 var underlay = this.overlay.underlay; 34828 if (underlay && YAHOO.env.ua.ie) { 34829 var hasFilters = (underlay.filters && underlay.filters.length > 0); 34830 if(hasFilters) { 34831 Dom.addClass(overlay.element, "yui-effect-fade"); 34832 } 34833 } 34834 }; 34835 34836 fade.handleUnderlayComplete = function() { 34837 var underlay = this.overlay.underlay; 34838 if (underlay && YAHOO.env.ua.ie) { 34839 Dom.removeClass(overlay.element, "yui-effect-fade"); 34840 } 34841 }; 34842 34843 fade.handleStartAnimateIn = function (type, args, obj) { 34844 obj.overlay._fadingIn = true; 34845 34846 Dom.addClass(obj.overlay.element, "hide-select"); 34847 34848 if (!obj.overlay.underlay) { 34849 obj.overlay.cfg.refireEvent("underlay"); 34850 } 34851 34852 obj.handleUnderlayStart(); 34853 34854 obj.overlay._setDomVisibility(true); 34855 Dom.setStyle(obj.overlay.element, "opacity", 0); 34856 }; 34857 34858 fade.handleCompleteAnimateIn = function (type,args,obj) { 34859 obj.overlay._fadingIn = false; 34860 34861 Dom.removeClass(obj.overlay.element, "hide-select"); 34862 34863 if (obj.overlay.element.style.filter) { 34864 obj.overlay.element.style.filter = null; 34865 } 34866 34867 obj.handleUnderlayComplete(); 34868 34869 obj.overlay.cfg.refireEvent("iframe"); 34870 obj.animateInCompleteEvent.fire(); 34871 }; 34872 34873 fade.handleStartAnimateOut = function (type, args, obj) { 34874 obj.overlay._fadingOut = true; 34875 Dom.addClass(obj.overlay.element, "hide-select"); 34876 obj.handleUnderlayStart(); 34877 }; 34878 34879 fade.handleCompleteAnimateOut = function (type, args, obj) { 34880 obj.overlay._fadingOut = false;
34881 Dom.removeClass(obj.overlay.element, "hide-select"); 34882 34883 if (obj.overlay.element.style.filter) { 34884 obj.overlay.element.style.filter = null; 34885 } 34886 obj.overlay._setDomVisibility(false); 34887 Dom.setStyle(obj.overlay.element, "opacity", 1); 34888 34889 obj.handleUnderlayComplete(); 34890 34891 obj.overlay.cfg.refireEvent("iframe"); 34892 obj.animateOutCompleteEvent.fire(); 34893 }; 34894 34895 fade.init(); 34896 return fade; 34897 }; 34898 34899 34900 /** 34901 * A pre-configured ContainerEffect instance that can be used for sliding an 34902 * overlay in and out. 34903 * @method SLIDE 34904 * @static 34905 * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate 34906 * @param {Number} dur The duration of the animation 34907 * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object 34908 */ 34909 ContainerEffect.SLIDE = function (overlay, dur) { 34910 var Easing = YAHOO.util.Easing, 34911 34912 x = overlay.cfg.getProperty("x") || Dom.getX(overlay.element), 34913 y = overlay.cfg.getProperty("y") || Dom.getY(overlay.element), 34914 clientWidth = Dom.getClientWidth(), 34915 offsetWidth = overlay.element.offsetWidth, 34916 34917 sin = { 34918 attributes: { points: { to: [x, y] } }, 34919 duration: dur, 34920 method: Easing.easeIn 34921 }, 34922 34923 sout = { 34924 attributes: { points: { to: [(clientWidth + 25), y] } }, 34925 duration: dur, 34926 method: Easing.easeOut 34927 }, 34928 34929 slide = new ContainerEffect(overlay, sin, sout, overlay.element, YAHOO.util.Motion); 34930 34931 slide.handleStartAnimateIn = function (type,args,obj) { 34932 obj.overlay.element.style.left = ((-25) - offsetWidth) + "px"; 34933 obj.overlay.element.style.top = y + "px"; 34934 }; 34935 34936 slide.handleTweenAnimateIn = function (type, args, obj) { 34937 34938 var pos = Dom.getXY(obj.overlay.element), 34939 currentX = pos[0], 34940 currentY = pos[1]; 34941 34942 if (Dom.getStyle(obj.overlay.element, "visibility") == 34943 "hidden" && currentX < x) { 34944 34945 obj.overlay._setDomVisibility(true); 34946 34947 } 34948 34949 obj.overlay.cfg.setProperty("xy", [currentX, currentY], true); 34950 obj.overlay.cfg.refireEvent("iframe"); 34951 }; 34952 34953 slide.handleCompleteAnimateIn = function (type, args, obj) { 34954 obj.overlay.cfg.setProperty("xy", [x, y], true); 34955 obj.startX = x; 34956 obj.startY = y; 34957 obj.overlay.cfg.refireEvent("iframe"); 34958 obj.animateInCompleteEvent.fire(); 34959 }; 34960 34961 slide.handleStartAnimateOut = function (type, args, obj) { 34962 34963 var vw = Dom.getViewportWidth(), 34964 pos = Dom.getXY(obj.overlay.element), 34965 yso = pos[1]; 34966 34967 obj.animOut.attributes.points.to = [(vw + 25), yso]; 34968 }; 34969 34970 slide.handleTweenAnimateOut = function (type, args, obj) { 34971 34972 var pos = Dom.getXY(obj.overlay.element), 34973 xto = pos[0], 34974 yto = pos[1]; 34975 34976 obj.overlay.cfg.setProperty("xy", [xto, yto], true); 34977 obj.overlay.cfg.refireEvent("iframe"); 34978 }; 34979 34980 slide.handleCompleteAnimateOut = function (type, args, obj) { 34981 obj.overlay._setDomVisibility(false); 34982 34983 obj.overlay.cfg.setProperty("xy", [x, y]); 34984 obj.animateOutCompleteEvent.fire(); 34985 }; 34986 34987 slide.init(); 34988 return slide; 34989 }; 34990 34991 ContainerEffect.prototype = { 34992 34993 /** 34994 * Initializes the animation classes and events. 34995 * @method init 34996 */ 34997 init: function () { 34998 34999 this.beforeAnimateInEvent = this.createEvent("beforeAnimateIn"); 35000 this.beforeAnimateInEvent.signature = CustomEvent.LIST; 35001 35002 this.beforeAnimateOutEvent = this.createEvent("beforeAnimateOut"); 35003 this.beforeAnimateOutEvent.signature = CustomEvent.LIST; 35004 35005 this.animateInCompleteEvent = this.createEvent("animateInComplete"); 35006 this.animateInCompleteEvent.signature = CustomEvent.LIST; 35007 35008 this.animateOutCompleteEvent = this.createEvent("animateOutComplete"); 35009 this.animateOutCompleteEvent.signature = CustomEvent.LIST; 35010 35011 this.animIn = new this.animClass( 35012 this.targetElement, 35013 this.attrIn.attributes, 35014 this.attrIn.duration, 35015 this.attrIn.method); 35016 35017 this.animIn.onStart.subscribe(this.handleStartAnimateIn, this); 35018 this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this); 35019 this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this); 35020 35021 this.animOut = new this.animClass( 35022 this.targetElement, 35023 this.attrOut.attributes, 35024 this.attrOut.duration, 35025 this.attrOut.method); 35026 35027 this.animOut.onStart.subscribe(this.handleStartAnimateOut, this); 35028 this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this); 35029 this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this); 35030 35031 }, 35032 35033 /** 35034 * Triggers the in-animation. 35035 * @method animateIn 35036 */ 35037 animateIn: function () { 35038 this._stopAnims(this.lastFrameOnStop); 35039 this.beforeAnimateInEvent.fire(); 35040 this.animIn.animate(); 35041 }, 35042 35043 /**
35044 * Triggers the out-animation. 35045 * @method animateOut 35046 */ 35047 animateOut: function () { 35048 this._stopAnims(this.lastFrameOnStop); 35049 this.beforeAnimateOutEvent.fire(); 35050 this.animOut.animate(); 35051 }, 35052 35053 /** 35054 * Flag to define whether Anim should jump to the last frame, 35055 * when animateIn or animateOut is stopped. 35056 * 35057 * @property lastFrameOnStop 35058 * @default true 35059 * @type boolean 35060 */ 35061 lastFrameOnStop : true, 35062 35063 /** 35064 * Stops both animIn and animOut instances, if in progress. 35065 * 35066 * @method _stopAnims 35067 * @param {boolean} finish If true, animation will jump to final frame. 35068 * @protected 35069 */ 35070 _stopAnims : function(finish) { 35071 if (this.animOut && this.animOut.isAnimated()) { 35072 this.animOut.stop(finish); 35073 } 35074 35075 if (this.animIn && this.animIn.isAnimated()) { 35076 this.animIn.stop(finish); 35077 } 35078 }, 35079 35080 /** 35081 * The default onStart handler for the in-animation. 35082 * @method handleStartAnimateIn 35083 * @param {String} type The CustomEvent type 35084 * @param {Object[]} args The CustomEvent arguments 35085 * @param {Object} obj The scope object 35086 */ 35087 handleStartAnimateIn: function (type, args, obj) { }, 35088 35089 /** 35090 * The default onTween handler for the in-animation. 35091 * @method handleTweenAnimateIn 35092 * @param {String} type The CustomEvent type 35093 * @param {Object[]} args The CustomEvent arguments 35094 * @param {Object} obj The scope object 35095 */ 35096 handleTweenAnimateIn: function (type, args, obj) { }, 35097 35098 /** 35099 * The default onComplete handler for the in-animation. 35100 * @method handleCompleteAnimateIn 35101 * @param {String} type The CustomEvent type 35102 * @param {Object[]} args The CustomEvent arguments 35103 * @param {Object} obj The scope object 35104 */ 35105 handleCompleteAnimateIn: function (type, args, obj) { }, 35106 35107 /** 35108 * The default onStart handler for the out-animation. 35109 * @method handleStartAnimateOut 35110 * @param {String} type The CustomEvent type 35111 * @param {Object[]} args The CustomEvent arguments 35112 * @param {Object} obj The scope object 35113 */ 35114 handleStartAnimateOut: function (type, args, obj) { }, 35115 35116 /** 35117 * The default onTween handler for the out-animation. 35118 * @method handleTweenAnimateOut 35119 * @param {String} type The CustomEvent type 35120 * @param {Object[]} args The CustomEvent arguments 35121 * @param {Object} obj The scope object 35122 */ 35123 handleTweenAnimateOut: function (type, args, obj) { }, 35124 35125 /** 35126 * The default onComplete handler for the out-animation. 35127 * @method handleCompleteAnimateOut 35128 * @param {String} type The CustomEvent type 35129 * @param {Object[]} args The CustomEvent arguments 35130 * @param {Object} obj The scope object 35131 */ 35132 handleCompleteAnimateOut: function (type, args, obj) { }, 35133 35134 /** 35135 * Returns a string representation of the object. 35136 * @method toString 35137 * @return {String} The string representation of the ContainerEffect 35138 */ 35139 toString: function () { 35140 var output = "ContainerEffect"; 35141 if (this.overlay) { 35142 output += " [" + this.overlay.toString() + "]"; 35143 } 35144 return output; 35145 } 35146 }; 35147 35148 YAHOO.lang.augmentProto(ContainerEffect, YAHOO.util.EventProvider); 35149 35150})(); 35151YAHOO.register("container", YAHOO.widget.Module, {version: "2.9.0", build: "2800"}); 35152/* 35153Copyright (c) 2011, Yahoo! Inc. All rights reserved. 35154Code licensed under the BSD License: 35155http://developer.yahoo.com/yui/license.html 35156version: 2.9.0 35157*/ 35158 35159 35160/** 35161* @module menu 35162* @description <p>The Menu family of components features a collection of 35163* controls that make it easy to add menus to your website or web application. 35164* With the Menu Controls you can create website fly-out menus, customized 35165* context menus, or application-style menu bars with just a small amount of 35166* scripting.</p><p>The Menu family of controls features:</p> 35167* <ul> 35168* <li>Keyboard and mouse navigation.</li> 35169* <li>A rich event model that provides access to all of a menu's
35170* interesting moments.</li> 35171* <li>Support for 35172* <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive 35173* Enhancement</a>; Menus can be created from simple, 35174* semantic markup on the page or purely through JavaScript.</li> 35175* </ul> 35176* @title Menu 35177* @namespace YAHOO.widget 35178* @requires Event, Dom, Container 35179*/ 35180(function () { 35181 35182 var UA = YAHOO.env.ua, 35183 Dom = YAHOO.util.Dom, 35184 Event = YAHOO.util.Event, 35185 Lang = YAHOO.lang, 35186 35187 _DIV = "DIV", 35188 _HD = "hd", 35189 _BD = "bd", 35190 _FT = "ft", 35191 _LI = "LI", 35192 _DISABLED = "disabled", 35193 _MOUSEOVER = "mouseover", 35194 _MOUSEOUT = "mouseout", 35195 _MOUSEDOWN = "mousedown", 35196 _MOUSEUP = "mouseup", 35197 _CLICK = "click", 35198 _TOUCHSTART = "touchstart", 35199 _TOUCHEND = "touchend", 35200 _KEYDOWN = "keydown", 35201 _KEYUP = "keyup", 35202 _KEYPRESS = "keypress", 35203 _CLICK_TO_HIDE = "clicktohide", 35204 _POSITION = "position", 35205 _DYNAMIC = "dynamic", 35206 _SHOW_DELAY = "showdelay", 35207 _SELECTED = "selected", 35208 _VISIBLE = "visible", 35209 _UL = "UL", 35210 _MENUMANAGER = "MenuManager"; 35211 35212 35213 /** 35214 * Singleton that manages a collection of all menus and menu items. Listens 35215 * for DOM events at the document level and dispatches the events to the 35216 * corresponding menu or menu item. 35217 * 35218 * @namespace YAHOO.widget 35219 * @class MenuManager 35220 * @static 35221 */ 35222 YAHOO.widget.MenuManager = function () { 35223 35224 // Private member variables 35225 35226 35227 // Flag indicating if the DOM event handlers have been attached 35228 35229 var m_bInitializedEventHandlers = false, 35230 35231 35232 // Collection of menus 35233 35234 m_oMenus = {}, 35235 35236 35237 // Collection of visible menus 35238 35239 m_oVisibleMenus = {}, 35240 35241 35242 // Collection of menu items 35243 35244 m_oItems = {}, 35245 35246 35247 // Map of DOM event types to their equivalent CustomEvent types 35248 35249 m_oEventTypes = { 35250 "click": "clickEvent", 35251 "mousedown": "mouseDownEvent", 35252 "mouseup": "mouseUpEvent", 35253 "mouseover": "mouseOverEvent", 35254 "mouseout": "mouseOutEvent", 35255 "touchstart": "mouseOverEvent", 35256 "touchend": "mouseUpEvent", 35257 "keydown": "keyDownEvent", 35258 "keyup": "keyUpEvent", 35259 "keypress": "keyPressEvent", 35260 "focus": "focusEvent", 35261 "focusin": "focusEvent", 35262 "blur": "blurEvent", 35263 "focusout": "blurEvent" 35264 }, 35265 35266 35267 m_oFocusedMenuItem = null; 35268 35269 35270 35271 // Private methods 35272 35273 35274 /** 35275 * @method getMenuRootElement 35276 * @description Finds the root DIV node of a menu or the root LI node of 35277 * a menu item. 35278 * @private 35279 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 35280 * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object 35281 * specifying an HTML element. 35282 */ 35283 function getMenuRootElement(p_oElement) { 35284 35285 var oParentNode, 35286 returnVal; 35287 35288 if (p_oElement && p_oElement.tagName) { 35289 35290 switch (p_oElement.tagName.toUpperCase()) { 35291 35292 case _DIV: 35293 35294 oParentNode = p_oElement.parentNode; 35295 35296 // Check if the DIV is the inner "body" node of a menu 35297 35298 if (( 35299 Dom.hasClass(p_oElement, _HD) || 35300 Dom.hasClass(p_oElement, _BD) || 35301 Dom.hasClass(p_oElement, _FT) 35302 ) && 35303 oParentNode && 35304 oParentNode.tagName && 35305 oParentNode.tagName.toUpperCase() == _DIV) { 35306 35307 returnVal = oParentNode; 35308 35309 } 35310 else { 35311 35312 returnVal = p_oElement; 35313 35314 } 35315 35316 break; 35317 35318 case _LI: 35319 35320 returnVal = p_oElement; 35321 35322 break; 35323 35324 default: 35325 35326 oParentNode = p_oElement.parentNode; 35327 35328 if (oParentNode) { 35329 35330 returnVal = getMenuRootElement(oParentNode); 35331 35332 } 35333 35334 break; 35335 35336 } 35337 35338 } 35339 35340 return returnVal; 35341 35342 } 35343 35344 35345 35346 // Private event handlers 35347 35348 35349 /** 35350 * @method onDOMEvent
35351 * @description Generic, global event handler for all of a menu's 35352 * DOM-based events. This listens for events against the document 35353 * object. If the target of a given event is a member of a menu or 35354 * menu item's DOM, the instance's corresponding Custom Event is fired. 35355 * @private 35356 * @param {Event} p_oEvent Object representing the DOM event object 35357 * passed back by the event utility (YAHOO.util.Event). 35358 */ 35359 function onDOMEvent(p_oEvent) { 35360 35361 // Get the target node of the DOM event 35362 35363 var oTarget = Event.getTarget(p_oEvent), 35364 35365 // See if the target of the event was a menu, or a menu item 35366 35367 oElement = getMenuRootElement(oTarget), 35368 bFireEvent = true, 35369 sEventType = p_oEvent.type, 35370 sCustomEventType, 35371 sTagName, 35372 sId, 35373 oMenuItem, 35374 oMenu; 35375 35376 35377 if (oElement) { 35378 35379 sTagName = oElement.tagName.toUpperCase(); 35380 35381 if (sTagName == _LI) { 35382 35383 sId = oElement.id; 35384 35385 if (sId && m_oItems[sId]) { 35386 35387 oMenuItem = m_oItems[sId]; 35388 oMenu = oMenuItem.parent; 35389 35390 } 35391 35392 } 35393 else if (sTagName == _DIV) { 35394 35395 if (oElement.id) { 35396 35397 oMenu = m_oMenus[oElement.id]; 35398 35399 } 35400 35401 } 35402 35403 } 35404 35405 35406 if (oMenu) { 35407 35408 sCustomEventType = m_oEventTypes[sEventType]; 35409 35410 /* 35411 There is an inconsistency between Firefox for Mac OS X and 35412 Firefox Windows & Linux regarding the triggering of the 35413 display of the browser's context menu and the subsequent 35414 firing of the "click" event. In Firefox for Windows & Linux, 35415 when the user triggers the display of the browser's context 35416 menu the "click" event also fires for the document object, 35417 even though the "click" event did not fire for the element 35418 that was the original target of the "contextmenu" event. 35419 This is unique to Firefox on Windows & Linux. For all 35420 other A-Grade browsers, including Firefox for Mac OS X, the 35421 "click" event doesn't fire for the document object. 35422 35423 This bug in Firefox for Windows affects Menu, as Menu 35424 instances listen for events at the document level and 35425 dispatches Custom Events of the same name. Therefore users 35426 of Menu will get an unwanted firing of the "click" 35427 custom event. The following line fixes this bug. 35428 */ 35429 35430 35431 35432 if (sEventType == "click" && 35433 (UA.gecko && oMenu.platform != "mac") && 35434 p_oEvent.button > 0) { 35435 35436 bFireEvent = false; 35437 35438 } 35439 35440 // Fire the Custom Event that corresponds the current DOM event 35441 35442 if (bFireEvent && oMenuItem && !oMenuItem.cfg.getProperty(_DISABLED)) { 35443 oMenuItem[sCustomEventType].fire(p_oEvent); 35444 } 35445 35446 if (bFireEvent) { 35447 oMenu[sCustomEventType].fire(p_oEvent, oMenuItem); 35448 } 35449 35450 } 35451 else if (sEventType == _MOUSEDOWN) { 35452 35453 /* 35454 If the target of the event wasn't a menu, hide all 35455 dynamically positioned menus 35456 */ 35457 35458 for (var i in m_oVisibleMenus) { 35459 35460 if (Lang.hasOwnProperty(m_oVisibleMenus, i)) { 35461 35462 oMenu = m_oVisibleMenus[i]; 35463 35464 if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) &&
35465 !(oMenu instanceof YAHOO.widget.MenuBar) && 35466 oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) { 35467 35468 oMenu.hide(); 35469 35470 // In IE when the user mouses down on a focusable 35471 // element that element will be focused and become 35472 // the "activeElement". 35473 // (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx) 35474 // However, there is a bug in IE where if there is 35475 // a positioned element with a focused descendant 35476 // that is hidden in response to the mousedown 35477 // event, the target of the mousedown event will 35478 // appear to have focus, but will not be set as 35479 // the activeElement. This will result in the 35480 // element not firing key events, even though it 35481 // appears to have focus. The following call to 35482 // "setActive" fixes this bug. 35483 35484 if (UA.ie && oTarget.focus && (UA.ie < 9)) { 35485 oTarget.setActive(); 35486 } 35487 35488 } 35489 else { 35490 35491 if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) { 35492 35493 oMenu._cancelShowDelay(); 35494 35495 } 35496 35497 35498 if (oMenu.activeItem) { 35499 35500 oMenu.activeItem.blur(); 35501 oMenu.activeItem.cfg.setProperty(_SELECTED, false); 35502 35503 oMenu.activeItem = null; 35504 35505 } 35506 35507 } 35508 35509 } 35510 35511 } 35512 35513 } 35514 else if (sEventType == _TOUCHEND) { 35515 for (var i in m_oVisibleMenus) { 35516 if (Lang.hasOwnProperty(m_oVisibleMenus, i)) { 35517 oMenu = m_oVisibleMenus[i]; 35518 oMenu.hide(); 35519 } 35520 } 35521 } 35522 35523 } 35524 35525 35526 /** 35527 * @method onMenuDestroy 35528 * @description "destroy" event handler for a menu. 35529 * @private 35530 * @param {String} p_sType String representing the name of the event 35531 * that was fired. 35532 * @param {Array} p_aArgs Array of arguments sent when the event 35533 * was fired. 35534 * @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event. 35535 */ 35536 function onMenuDestroy(p_sType, p_aArgs, p_oMenu) { 35537 35538 if (m_oMenus[p_oMenu.id]) { 35539 35540 this.removeMenu(p_oMenu); 35541 35542 } 35543 35544 } 35545 35546 35547 /** 35548 * @method onMenuFocus 35549 * @description "focus" event handler for a MenuItem instance. 35550 * @private 35551 * @param {String} p_sType String representing the name of the event 35552 * that was fired. 35553 * @param {Array} p_aArgs Array of arguments sent when the event 35554 * was fired. 35555 */ 35556 function onMenuFocus(p_sType, p_aArgs) { 35557 35558 var oItem = p_aArgs[1]; 35559 35560 if (oItem) { 35561 35562 m_oFocusedMenuItem = oItem; 35563 35564 } 35565 35566 } 35567 35568 35569 /** 35570 * @method onMenuBlur 35571 * @description "blur" event handler for a MenuItem instance. 35572 * @private 35573 * @param {String} p_sType String representing the name of the event 35574 * that was fired. 35575 * @param {Array} p_aArgs Array of arguments sent when the event 35576 * was fired. 35577 */ 35578 function onMenuBlur(p_sType, p_aArgs) { 35579 35580 m_oFocusedMenuItem = null; 35581 35582 } 35583 35584 35585 /** 35586 * @method onMenuVisibleConfigChange
35587 * @description Event handler for when the "visible" configuration 35588 * property of a Menu instance changes. 35589 * @private 35590 * @param {String} p_sType String representing the name of the event 35591 * that was fired. 35592 * @param {Array} p_aArgs Array of arguments sent when the event 35593 * was fired. 35594 */ 35595 function onMenuVisibleConfigChange(p_sType, p_aArgs) { 35596 35597 var bVisible = p_aArgs[0], 35598 sId = this.id; 35599 35600 if (bVisible) { 35601 35602 m_oVisibleMenus[sId] = this; 35603 35604 YAHOO.log(this + " added to the collection of visible menus.", 35605 "info", _MENUMANAGER); 35606 35607 } 35608 else if (m_oVisibleMenus[sId]) { 35609 35610 delete m_oVisibleMenus[sId]; 35611 35612 YAHOO.log(this + " removed from the collection of visible menus.", 35613 "info", _MENUMANAGER); 35614 35615 } 35616 35617 } 35618 35619 35620 /** 35621 * @method onItemDestroy 35622 * @description "destroy" event handler for a MenuItem instance. 35623 * @private 35624 * @param {String} p_sType String representing the name of the event 35625 * that was fired. 35626 * @param {Array} p_aArgs Array of arguments sent when the event 35627 * was fired. 35628 */ 35629 function onItemDestroy(p_sType, p_aArgs) { 35630 35631 removeItem(this); 35632 35633 } 35634 35635 35636 /** 35637 * @method removeItem 35638 * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems. 35639 * @private 35640 * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed. 35641 */ 35642 function removeItem(p_oMenuItem) { 35643 35644 var sId = p_oMenuItem.id; 35645 35646 if (sId && m_oItems[sId]) { 35647 35648 if (m_oFocusedMenuItem == p_oMenuItem) { 35649 35650 m_oFocusedMenuItem = null; 35651 35652 } 35653 35654 delete m_oItems[sId]; 35655 35656 p_oMenuItem.destroyEvent.unsubscribe(onItemDestroy); 35657 35658 YAHOO.log(p_oMenuItem + " successfully unregistered.", "info", _MENUMANAGER); 35659 35660 } 35661 35662 } 35663 35664 35665 /** 35666 * @method onItemAdded 35667 * @description "itemadded" event handler for a Menu instance. 35668 * @private 35669 * @param {String} p_sType String representing the name of the event 35670 * that was fired. 35671 * @param {Array} p_aArgs Array of arguments sent when the event 35672 * was fired. 35673 */ 35674 function onItemAdded(p_sType, p_aArgs) { 35675 35676 var oItem = p_aArgs[0], 35677 sId; 35678 35679 if (oItem instanceof YAHOO.widget.MenuItem) { 35680 35681 sId = oItem.id; 35682 35683 if (!m_oItems[sId]) { 35684 35685 m_oItems[sId] = oItem; 35686 35687 oItem.destroyEvent.subscribe(onItemDestroy); 35688 35689 YAHOO.log(oItem + " successfully registered.", "info", _MENUMANAGER); 35690 35691 } 35692 35693 } 35694 35695 } 35696 35697 35698 return { 35699 35700 // Privileged methods 35701 35702 35703 /** 35704 * @method addMenu 35705 * @description Adds a menu to the collection of known menus. 35706 * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu 35707 * instance to be added. 35708 */ 35709 addMenu: function (p_oMenu) { 35710 35711 var oDoc; 35712 35713 if (p_oMenu instanceof YAHOO.widget.Menu && p_oMenu.id && 35714 !m_oMenus[p_oMenu.id]) { 35715 35716 m_oMenus[p_oMenu.id] = p_oMenu; 35717 35718 35719 if (!m_bInitializedEventHandlers) { 35720 35721 oDoc = document; 35722 35723 Event.on(oDoc, _MOUSEOVER, onDOMEvent, this, true); 35724 Event.on(oDoc, _MOUSEOUT, onDOMEvent, this, true); 35725 Event.on(oDoc, _MOUSEDOWN, onDOMEvent, this, true); 35726 Event.on(oDoc, _MOUSEUP, onDOMEvent, this, true); 35727 Event.on(oDoc, _CLICK, onDOMEvent, this, true); 35728 Event.on(oDoc, _TOUCHSTART, onDOMEvent, this, true); 35729 Event.on(oDoc, _TOUCHEND, onDOMEvent, this, true); 35730 Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true); 35731 Event.on(oDoc, _KEYUP, onDOMEvent, this, true); 35732 Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true); 35733 35734 Event.onFocus(oDoc, onDOMEvent, this, true); 35735 Event.onBlur(oDoc, onDOMEvent, this, true); 35736 35737 m_bInitializedEventHandlers = true; 35738 35739 YAHOO.log("DOM event handlers initialized.", "info", _MENUMANAGER); 35740 35741 } 35742 35743 p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange); 35744 p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this); 35745 p_oMenu.itemAddedEvent.subscribe(onItemAdded);
35746 p_oMenu.focusEvent.subscribe(onMenuFocus); 35747 p_oMenu.blurEvent.subscribe(onMenuBlur); 35748 35749 YAHOO.log(p_oMenu + " successfully registered.", "info", _MENUMANAGER); 35750 35751 } 35752 35753 }, 35754 35755 35756 /** 35757 * @method removeMenu 35758 * @description Removes a menu from the collection of known menus. 35759 * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu 35760 * instance to be removed. 35761 */ 35762 removeMenu: function (p_oMenu) { 35763 35764 var sId, 35765 aItems, 35766 i; 35767 35768 if (p_oMenu) { 35769 35770 sId = p_oMenu.id; 35771 35772 if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) { 35773 35774 // Unregister each menu item 35775 35776 aItems = p_oMenu.getItems(); 35777 35778 if (aItems && aItems.length > 0) { 35779 35780 i = aItems.length - 1; 35781 35782 do { 35783 35784 removeItem(aItems[i]); 35785 35786 } 35787 while (i--); 35788 35789 } 35790 35791 35792 // Unregister the menu 35793 35794 delete m_oMenus[sId]; 35795 35796 YAHOO.log(p_oMenu + " successfully unregistered.", "info", _MENUMANAGER); 35797 35798 35799 /* 35800 Unregister the menu from the collection of 35801 visible menus 35802 */ 35803 35804 if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) { 35805 35806 delete m_oVisibleMenus[sId]; 35807 35808 YAHOO.log(p_oMenu + " unregistered from the" + 35809 " collection of visible menus.", "info", _MENUMANAGER); 35810 35811 } 35812 35813 35814 // Unsubscribe event listeners 35815 35816 if (p_oMenu.cfg) { 35817 35818 p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE, 35819 onMenuVisibleConfigChange); 35820 35821 } 35822 35823 p_oMenu.destroyEvent.unsubscribe(onMenuDestroy, 35824 p_oMenu); 35825 35826 p_oMenu.itemAddedEvent.unsubscribe(onItemAdded); 35827 p_oMenu.focusEvent.unsubscribe(onMenuFocus); 35828 p_oMenu.blurEvent.unsubscribe(onMenuBlur); 35829 35830 } 35831 35832 } 35833 35834 }, 35835 35836 35837 /** 35838 * @method hideVisible 35839 * @description Hides all visible, dynamically positioned menus 35840 * (excluding instances of YAHOO.widget.MenuBar). 35841 */ 35842 hideVisible: function () { 35843 35844 var oMenu; 35845 35846 for (var i in m_oVisibleMenus) { 35847 35848 if (Lang.hasOwnProperty(m_oVisibleMenus, i)) { 35849 35850 oMenu = m_oVisibleMenus[i]; 35851 35852 if (!(oMenu instanceof YAHOO.widget.MenuBar) && 35853 oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) { 35854 35855 oMenu.hide(); 35856 35857 } 35858 35859 } 35860 35861 } 35862 35863 }, 35864 35865 35866 /** 35867 * @method getVisible 35868 * @description Returns a collection of all visible menus registered 35869 * with the menu manger. 35870 * @return {Object} 35871 */ 35872 getVisible: function () { 35873 35874 return m_oVisibleMenus; 35875 35876 }, 35877 35878 35879 /** 35880 * @method getMenus 35881 * @description Returns a collection of all menus registered with the 35882 * menu manger. 35883 * @return {Object} 35884 */ 35885 getMenus: function () { 35886 35887 return m_oMenus; 35888 35889 }, 35890 35891 35892 /** 35893 * @method getMenu
35894 * @description Returns a menu with the specified id. 35895 * @param {String} p_sId String specifying the id of the 35896 * <code><div></code> element representing the menu to 35897 * be retrieved. 35898 * @return {YAHOO.widget.Menu} 35899 */ 35900 getMenu: function (p_sId) { 35901 35902 var returnVal; 35903 35904 if (p_sId in m_oMenus) { 35905 35906 returnVal = m_oMenus[p_sId]; 35907 35908 } 35909 35910 return returnVal; 35911 35912 }, 35913 35914 35915 /** 35916 * @method getMenuItem 35917 * @description Returns a menu item with the specified id. 35918 * @param {String} p_sId String specifying the id of the 35919 * <code><li></code> element representing the menu item to 35920 * be retrieved. 35921 * @return {YAHOO.widget.MenuItem} 35922 */ 35923 getMenuItem: function (p_sId) { 35924 35925 var returnVal; 35926 35927 if (p_sId in m_oItems) { 35928 35929 returnVal = m_oItems[p_sId]; 35930 35931 } 35932 35933 return returnVal; 35934 35935 }, 35936 35937 35938 /** 35939 * @method getMenuItemGroup 35940 * @description Returns an array of menu item instances whose 35941 * corresponding <code><li></code> elements are child 35942 * nodes of the <code><ul></code> element with the 35943 * specified id. 35944 * @param {String} p_sId String specifying the id of the 35945 * <code><ul></code> element representing the group of 35946 * menu items to be retrieved. 35947 * @return {Array} 35948 */ 35949 getMenuItemGroup: function (p_sId) { 35950 35951 var oUL = Dom.get(p_sId), 35952 aItems, 35953 oNode, 35954 oItem, 35955 sId, 35956 returnVal; 35957 35958 35959 if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) { 35960 35961 oNode = oUL.firstChild; 35962 35963 if (oNode) { 35964 35965 aItems = []; 35966 35967 do { 35968 35969 sId = oNode.id; 35970 35971 if (sId) { 35972 35973 oItem = this.getMenuItem(sId); 35974 35975 if (oItem) { 35976 35977 aItems[aItems.length] = oItem; 35978 35979 } 35980 35981 } 35982 35983 } 35984 while ((oNode = oNode.nextSibling)); 35985 35986 35987 if (aItems.length > 0) { 35988 35989 returnVal = aItems; 35990 35991 } 35992 35993 } 35994 35995 } 35996 35997 return returnVal; 35998 35999 }, 36000 36001 36002 /** 36003 * @method getFocusedMenuItem 36004 * @description Returns a reference to the menu item that currently 36005 * has focus. 36006 * @return {YAHOO.widget.MenuItem} 36007 */ 36008 getFocusedMenuItem: function () { 36009 36010 return m_oFocusedMenuItem; 36011 36012 }, 36013 36014 36015 /** 36016 * @method getFocusedMenu 36017 * @description Returns a reference to the menu that currently 36018 * has focus. 36019 * @return {YAHOO.widget.Menu} 36020 */ 36021 getFocusedMenu: function () { 36022 36023 var returnVal; 36024 36025 if (m_oFocusedMenuItem) { 36026 36027 returnVal = m_oFocusedMenuItem.parent.getRoot(); 36028 36029 } 36030 36031 return returnVal; 36032 36033 }, 36034 36035 36036 /** 36037 * @method toString 36038 * @description Returns a string representing the menu manager. 36039 * @return {String} 36040 */ 36041 toString: function () { 36042 36043 return _MENUMANAGER; 36044 36045 } 36046 36047 }; 36048 36049 }(); 36050 36051})(); 36052 36053 36054 36055(function () { 36056 36057 var Lang = YAHOO.lang, 36058 36059 // String constants 36060 36061 _MENU = "Menu", 36062 _DIV_UPPERCASE = "DIV", 36063 _DIV_LOWERCASE = "div", 36064 _ID = "id", 36065 _SELECT = "SELECT", 36066 _XY = "xy", 36067 _Y = "y", 36068 _UL_UPPERCASE = "UL", 36069 _UL_LOWERCASE = "ul", 36070 _FIRST_OF_TYPE = "first-of-type", 36071 _LI = "LI", 36072 _OPTGROUP = "OPTGROUP", 36073 _OPTION = "OPTION", 36074 _DISABLED = "disabled", 36075 _NONE = "none", 36076 _SELECTED = "selected", 36077 _GROUP_INDEX = "groupindex", 36078 _INDEX = "index", 36079 _SUBMENU = "submenu", 36080 _VISIBLE = "visible", 36081 _HIDE_DELAY = "hidedelay", 36082 _POSITION = "position", 36083 _DYNAMIC = "dynamic", 36084 _STATIC = "static", 36085 _DYNAMIC_STATIC = _DYNAMIC + "," + _STATIC, 36086 _URL = "url", 36087 _HASH = "#", 36088 _TARGET = "target", 36089 _MAX_HEIGHT = "maxheight",
36090 _TOP_SCROLLBAR = "topscrollbar", 36091 _BOTTOM_SCROLLBAR = "bottomscrollbar", 36092 _UNDERSCORE = "_", 36093 _TOP_SCROLLBAR_DISABLED = _TOP_SCROLLBAR + _UNDERSCORE + _DISABLED, 36094 _BOTTOM_SCROLLBAR_DISABLED = _BOTTOM_SCROLLBAR + _UNDERSCORE + _DISABLED, 36095 _MOUSEMOVE = "mousemove", 36096 _SHOW_DELAY = "showdelay", 36097 _SUBMENU_HIDE_DELAY = "submenuhidedelay", 36098 _IFRAME = "iframe", 36099 _CONSTRAIN_TO_VIEWPORT = "constraintoviewport", 36100 _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap", 36101 _SUBMENU_ALIGNMENT = "submenualignment", 36102 _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay", 36103 _CLICK_TO_HIDE = "clicktohide", 36104 _CONTAINER = "container", 36105 _SCROLL_INCREMENT = "scrollincrement", 36106 _MIN_SCROLL_HEIGHT = "minscrollheight", 36107 _CLASSNAME = "classname", 36108 _SHADOW = "shadow", 36109 _KEEP_OPEN = "keepopen", 36110 _HD = "hd", 36111 _HAS_TITLE = "hastitle", 36112 _CONTEXT = "context", 36113 _EMPTY_STRING = "", 36114 _MOUSEDOWN = "mousedown", 36115 _KEYDOWN = "keydown", 36116 _HEIGHT = "height", 36117 _WIDTH = "width", 36118 _PX = "px", 36119 _EFFECT = "effect", 36120 _MONITOR_RESIZE = "monitorresize", 36121 _DISPLAY = "display", 36122 _BLOCK = "block", 36123 _VISIBILITY = "visibility", 36124 _ABSOLUTE = "absolute", 36125 _ZINDEX = "zindex", 36126 _YUI_MENU_BODY_SCROLLED = "yui-menu-body-scrolled", 36127 _NON_BREAKING_SPACE = " ", 36128 _SPACE = " ", 36129 _MOUSEOVER = "mouseover", 36130 _MOUSEOUT = "mouseout", 36131 _ITEM_ADDED = "itemAdded", 36132 _ITEM_REMOVED = "itemRemoved", 36133 _HIDDEN = "hidden", 36134 _YUI_MENU_SHADOW = "yui-menu-shadow", 36135 _YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + "-visible", 36136 _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + _SPACE + _YUI_MENU_SHADOW_VISIBLE; 36137 36138 36139/** 36140* The Menu class creates a container that holds a vertical list representing 36141* a set of options or commands. Menu is the base class for all 36142* menu containers. 36143* @param {String} p_oElement String specifying the id attribute of the 36144* <code><div></code> element of the menu. 36145* @param {String} p_oElement String specifying the id attribute of the 36146* <code><select></code> element to be used as the data source 36147* for the menu. 36148* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 36149* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 36150* specifying the <code><div></code> element of the menu. 36151* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 36152* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 36153* Object specifying the <code><select></code> element to be used as 36154* the data source for the menu. 36155* @param {Object} p_oConfig Optional. Object literal specifying the 36156* configuration for the menu. See configuration class documentation for 36157* more details. 36158* @namespace YAHOO.widget 36159* @class Menu 36160* @constructor 36161* @extends YAHOO.widget.Overlay 36162*/ 36163YAHOO.widget.Menu = function (p_oElement, p_oConfig) { 36164 36165 if (p_oConfig) { 36166 this.parent = p_oConfig.parent; 36167 this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload; 36168 this.itemData = p_oConfig.itemData || p_oConfig.itemdata; 36169 } 36170 36171 YAHOO.widget.Menu.superclass.constructor.call(this, p_oElement, p_oConfig); 36172}; 36173 36174 36175 36176/** 36177* @method checkPosition 36178* @description Checks to make sure that the value of the "position" property 36179* is one of the supported strings. Returns true if the position is supported. 36180* @private 36181* @param {Object} p_sPosition String specifying the position of the menu. 36182* @return {Boolean} 36183*/ 36184function checkPosition(p_sPosition) { 36185 36186 var returnVal = false;
36187 36188 if (Lang.isString(p_sPosition)) { 36189 36190 returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1); 36191 36192 } 36193 36194 return returnVal; 36195 36196} 36197 36198 36199var Dom = YAHOO.util.Dom, 36200 Event = YAHOO.util.Event, 36201 Module = YAHOO.widget.Module, 36202 Overlay = YAHOO.widget.Overlay, 36203 Menu = YAHOO.widget.Menu, 36204 MenuManager = YAHOO.widget.MenuManager, 36205 CustomEvent = YAHOO.util.CustomEvent, 36206 UA = YAHOO.env.ua, 36207 36208 m_oShadowTemplate, 36209 36210 bFocusListenerInitialized = false, 36211 36212 oFocusedElement, 36213 36214 EVENT_TYPES = [ 36215 36216 ["mouseOverEvent", _MOUSEOVER], 36217 ["mouseOutEvent", _MOUSEOUT], 36218 ["mouseDownEvent", _MOUSEDOWN], 36219 ["mouseUpEvent", "mouseup"], 36220 ["clickEvent", "click"], 36221 ["keyPressEvent", "keypress"], 36222 ["keyDownEvent", _KEYDOWN], 36223 ["keyUpEvent", "keyup"], 36224 ["focusEvent", "focus"], 36225 ["blurEvent", "blur"], 36226 ["itemAddedEvent", _ITEM_ADDED], 36227 ["itemRemovedEvent", _ITEM_REMOVED] 36228 36229 ], 36230 36231 VISIBLE_CONFIG = { 36232 key: _VISIBLE, 36233 value: false, 36234 validator: Lang.isBoolean 36235 }, 36236 36237 CONSTRAIN_TO_VIEWPORT_CONFIG = { 36238 key: _CONSTRAIN_TO_VIEWPORT, 36239 value: true, 36240 validator: Lang.isBoolean, 36241 supercedes: [_IFRAME,"x",_Y,_XY] 36242 }, 36243 36244 PREVENT_CONTEXT_OVERLAP_CONFIG = { 36245 key: _PREVENT_CONTEXT_OVERLAP, 36246 value: true, 36247 validator: Lang.isBoolean, 36248 supercedes: [_CONSTRAIN_TO_VIEWPORT] 36249 }, 36250 36251 POSITION_CONFIG = { 36252 key: _POSITION, 36253 value: _DYNAMIC, 36254 validator: checkPosition, 36255 supercedes: [_VISIBLE, _IFRAME] 36256 }, 36257 36258 SUBMENU_ALIGNMENT_CONFIG = { 36259 key: _SUBMENU_ALIGNMENT, 36260 value: ["tl","tr"] 36261 }, 36262 36263 AUTO_SUBMENU_DISPLAY_CONFIG = { 36264 key: _AUTO_SUBMENU_DISPLAY, 36265 value: true, 36266 validator: Lang.isBoolean, 36267 suppressEvent: true 36268 }, 36269 36270 SHOW_DELAY_CONFIG = { 36271 key: _SHOW_DELAY, 36272 value: 250, 36273 validator: Lang.isNumber, 36274 suppressEvent: true 36275 }, 36276 36277 HIDE_DELAY_CONFIG = { 36278 key: _HIDE_DELAY, 36279 value: 0, 36280 validator: Lang.isNumber, 36281 suppressEvent: true 36282 }, 36283 36284 SUBMENU_HIDE_DELAY_CONFIG = { 36285 key: _SUBMENU_HIDE_DELAY, 36286 value: 250, 36287 validator: Lang.isNumber, 36288 suppressEvent: true 36289 }, 36290 36291 CLICK_TO_HIDE_CONFIG = { 36292 key: _CLICK_TO_HIDE, 36293 value: true, 36294 validator: Lang.isBoolean, 36295 suppressEvent: true 36296 }, 36297 36298 CONTAINER_CONFIG = { 36299 key: _CONTAINER, 36300 suppressEvent: true 36301 }, 36302 36303 SCROLL_INCREMENT_CONFIG = { 36304 key: _SCROLL_INCREMENT, 36305 value: 1, 36306 validator: Lang.isNumber, 36307 supercedes: [_MAX_HEIGHT], 36308 suppressEvent: true 36309 }, 36310 36311 MIN_SCROLL_HEIGHT_CONFIG = { 36312 key: _MIN_SCROLL_HEIGHT, 36313 value: 90, 36314 validator: Lang.isNumber, 36315 supercedes: [_MAX_HEIGHT], 36316 suppressEvent: true 36317 }, 36318 36319 MAX_HEIGHT_CONFIG = { 36320 key: _MAX_HEIGHT, 36321 value: 0, 36322 validator: Lang.isNumber, 36323 supercedes: [_IFRAME], 36324 suppressEvent: true 36325 }, 36326 36327 CLASS_NAME_CONFIG = { 36328 key: _CLASSNAME, 36329 value: null, 36330 validator: Lang.isString, 36331 suppressEvent: true 36332 }, 36333 36334 DISABLED_CONFIG = { 36335 key: _DISABLED, 36336 value: false, 36337 validator: Lang.isBoolean, 36338 suppressEvent: true 36339 }, 36340 36341 SHADOW_CONFIG = { 36342 key: _SHADOW, 36343 value: true, 36344 validator: Lang.isBoolean, 36345 suppressEvent: true, 36346 supercedes: [_VISIBLE] 36347 }, 36348 36349 KEEP_OPEN_CONFIG = { 36350 key: _KEEP_OPEN, 36351 value: false, 36352 validator: Lang.isBoolean 36353 }; 36354 36355 36356function onDocFocus(event) { 36357 36358 oFocusedElement = Event.getTarget(event); 36359 36360} 36361 36362 36363 36364YAHOO.lang.extend(Menu, Overlay, { 36365 36366 36367// Constants 36368 36369 36370/** 36371* @property CSS_CLASS_NAME 36372* @description String representing the CSS class(es) to be applied to the 36373* menu's <code><div></code> element. 36374* @default "yuimenu" 36375* @final 36376* @type String 36377*/ 36378CSS_CLASS_NAME: "yuimenu", 36379 36380 36381/** 36382* @property ITEM_TYPE 36383* @description Object representing the type of menu item to instantiate and 36384* add when parsing the child nodes (either <code><li></code> element, 36385* <code><optgroup></code> element or <code><option></code>) 36386* of the menu's source HTML element.
36387* @default YAHOO.widget.MenuItem 36388* @final 36389* @type YAHOO.widget.MenuItem 36390*/ 36391ITEM_TYPE: null, 36392 36393 36394/** 36395* @property GROUP_TITLE_TAG_NAME 36396* @description String representing the tagname of the HTML element used to 36397* title the menu's item groups. 36398* @default H6 36399* @final 36400* @type String 36401*/ 36402GROUP_TITLE_TAG_NAME: "h6", 36403 36404 36405/** 36406* @property OFF_SCREEN_POSITION 36407* @description Array representing the default x and y position that a menu 36408* should have when it is positioned outside the viewport by the 36409* "poistionOffScreen" method. 36410* @default "-999em" 36411* @final 36412* @type String 36413*/ 36414OFF_SCREEN_POSITION: "-999em", 36415 36416 36417// Private properties 36418 36419 36420/** 36421* @property _useHideDelay 36422* @description Boolean indicating if the "mouseover" and "mouseout" event 36423* handlers used for hiding the menu via a call to "YAHOO.lang.later" have 36424* already been assigned. 36425* @default false 36426* @private 36427* @type Boolean 36428*/ 36429_useHideDelay: false, 36430 36431 36432/** 36433* @property _bHandledMouseOverEvent 36434* @description Boolean indicating the current state of the menu's 36435* "mouseover" event. 36436* @default false 36437* @private 36438* @type Boolean 36439*/ 36440_bHandledMouseOverEvent: false, 36441 36442 36443/** 36444* @property _bHandledMouseOutEvent 36445* @description Boolean indicating the current state of the menu's 36446* "mouseout" event. 36447* @default false 36448* @private 36449* @type Boolean 36450*/ 36451_bHandledMouseOutEvent: false, 36452 36453 36454/** 36455* @property _aGroupTitleElements 36456* @description Array of HTML element used to title groups of menu items. 36457* @default [] 36458* @private 36459* @type Array 36460*/ 36461_aGroupTitleElements: null, 36462 36463 36464/** 36465* @property _aItemGroups 36466* @description Multi-dimensional Array representing the menu items as they 36467* are grouped in the menu. 36468* @default [] 36469* @private 36470* @type Array 36471*/ 36472_aItemGroups: null, 36473 36474 36475/** 36476* @property _aListElements 36477* @description Array of <code><ul></code> elements, each of which is 36478* the parent node for each item's <code><li></code> element. 36479* @default [] 36480* @private 36481* @type Array 36482*/ 36483_aListElements: null, 36484 36485 36486/** 36487* @property _nCurrentMouseX 36488* @description The current x coordinate of the mouse inside the area of 36489* the menu. 36490* @default 0 36491* @private 36492* @type Number 36493*/ 36494_nCurrentMouseX: 0, 36495 36496 36497/** 36498* @property _bStopMouseEventHandlers 36499* @description Stops "mouseover," "mouseout," and "mousemove" event handlers 36500* from executing. 36501* @default false 36502* @private 36503* @type Boolean 36504*/ 36505_bStopMouseEventHandlers: false, 36506 36507 36508/** 36509* @property _sClassName 36510* @description The current value of the "classname" configuration attribute. 36511* @default null 36512* @private 36513* @type String 36514*/ 36515_sClassName: null, 36516 36517 36518 36519// Public properties 36520 36521 36522/** 36523* @property lazyLoad 36524* @description Boolean indicating if the menu's "lazy load" feature is 36525* enabled. If set to "true," initialization and rendering of the menu's 36526* items will be deferred until the first time it is made visible. This 36527* property should be set via the constructor using the configuration 36528* object literal. 36529* @default false 36530* @type Boolean 36531*/ 36532lazyLoad: false, 36533 36534 36535/** 36536* @property itemData 36537* @description Array of items to be added to the menu. The array can contain 36538* strings representing the text for each item to be created, object literals 36539* representing the menu item configuration properties, or MenuItem instances. 36540* This property should be set via the constructor using the configuration 36541* object literal. 36542* @default null 36543* @type Array 36544*/ 36545itemData: null, 36546 36547 36548/** 36549* @property activeItem 36550* @description Object reference to the item in the menu that has is selected. 36551* @default null 36552* @type YAHOO.widget.MenuItem 36553*/ 36554activeItem: null, 36555 36556 36557/** 36558* @property parent 36559* @description Object reference to the menu's parent menu or menu item. 36560* This property can be set via the constructor using the configuration 36561* object literal. 36562* @default null 36563* @type YAHOO.widget.MenuItem 36564*/ 36565parent: null, 36566 36567 36568/** 36569* @property srcElement 36570* @description Object reference to the HTML element (either 36571* <code><select></code> or <code><div></code>) used to 36572* create the menu. 36573* @default null 36574* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 36575* level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a 36576* href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html. 36577* html#ID-22445964">HTMLDivElement</a> 36578*/ 36579srcElement: null, 36580 36581 36582 36583// Events 36584 36585 36586/** 36587* @event mouseOverEvent 36588* @description Fires when the mouse has entered the menu. Passes back 36589* the DOM Event object as an argument. 36590*/ 36591 36592 36593/** 36594* @event mouseOutEvent 36595* @description Fires when the mouse has left the menu. Passes back the DOM 36596* Event object as an argument. 36597* @type YAHOO.util.CustomEvent 36598*/ 36599 36600 36601/** 36602* @event mouseDownEvent 36603* @description Fires when the user mouses down on the menu. Passes back the 36604* DOM Event object as an argument. 36605* @type YAHOO.util.CustomEvent 36606*/ 36607 36608 36609/** 36610* @event mouseUpEvent 36611* @description Fires when the user releases a mouse button while the mouse is 36612* over the menu. Passes back the DOM Event object as an argument. 36613* @type YAHOO.util.CustomEvent 36614*/ 36615 36616 36617/** 36618* @event clickEvent 36619* @description Fires when the user clicks the on the menu. Passes back the 36620* DOM Event object as an argument. 36621* @type YAHOO.util.CustomEvent 36622*/ 36623 36624 36625/** 36626* @event keyPressEvent 36627* @description Fires when the user presses an alphanumeric key when one of the 36628* menu's items has focus. Passes back the DOM Event object as an argument. 36629* @type YAHOO.util.CustomEvent 36630*/ 36631 36632 36633/** 36634* @event keyDownEvent 36635* @description Fires when the user presses a key when one of the menu's items 36636* has focus. Passes back the DOM Event object as an argument. 36637* @type YAHOO.util.CustomEvent 36638*/ 36639 36640 36641/** 36642* @event keyUpEvent 36643* @description Fires when the user releases a key when one of the menu's items 36644* has focus. Passes back the DOM Event object as an argument. 36645* @type YAHOO.util.CustomEvent 36646*/ 36647 36648 36649/** 36650* @event itemAddedEvent 36651* @description Fires when an item is added to the menu. 36652* @type YAHOO.util.CustomEvent 36653*/ 36654 36655 36656/** 36657* @event itemRemovedEvent 36658* @description Fires when an item is removed to the menu. 36659* @type YAHOO.util.CustomEvent 36660*/ 36661 36662 36663/** 36664* @method init 36665* @description The Menu class's initialization method. This method is 36666* automatically called by the constructor, and sets up all DOM references 36667* for pre-existing markup, and creates required markup if it is not 36668* already present. 36669* @param {String} p_oElement String specifying the id attribute of the 36670* <code><div></code> element of the menu. 36671* @param {String} p_oElement String specifying the id attribute of the 36672* <code><select></code> element to be used as the data source 36673* for the menu. 36674* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 36675* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 36676* specifying the <code><div></code> element of the menu. 36677* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 36678* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 36679* Object specifying the <code><select></code> element to be used as 36680* the data source for the menu. 36681* @param {Object} p_oConfig Optional. Object literal specifying the 36682* configuration for the menu. See configuration class documentation for 36683* more details. 36684*/ 36685init: function (p_oElement, p_oConfig) { 36686 36687 this._aItemGroups = []; 36688 this._aListElements = []; 36689 this._aGroupTitleElements = []; 36690 36691 if (!this.ITEM_TYPE) { 36692
36693 this.ITEM_TYPE = YAHOO.widget.MenuItem; 36694 36695 } 36696 36697 36698 var oElement; 36699 36700 if (Lang.isString(p_oElement)) { 36701 36702 oElement = Dom.get(p_oElement); 36703 36704 } 36705 else if (p_oElement.tagName) { 36706 36707 oElement = p_oElement; 36708 36709 } 36710 36711 36712 if (oElement && oElement.tagName) { 36713 36714 switch(oElement.tagName.toUpperCase()) { 36715 36716 case _DIV_UPPERCASE: 36717 36718 this.srcElement = oElement; 36719 36720 if (!oElement.id) { 36721 36722 oElement.setAttribute(_ID, Dom.generateId()); 36723 36724 } 36725 36726 36727 /* 36728 Note: we don't pass the user config in here yet 36729 because we only want it executed once, at the lowest 36730 subclass level. 36731 */ 36732 36733 Menu.superclass.init.call(this, oElement); 36734 36735 this.beforeInitEvent.fire(Menu); 36736 36737 YAHOO.log("Source element: " + this.srcElement.tagName, "info", this.toString()); 36738 36739 break; 36740 36741 case _SELECT: 36742 36743 this.srcElement = oElement; 36744 36745 36746 /* 36747 The source element is not something that we can use 36748 outright, so we need to create a new Overlay 36749 36750 Note: we don't pass the user config in here yet 36751 because we only want it executed once, at the lowest 36752 subclass level. 36753 */ 36754 36755 Menu.superclass.init.call(this, Dom.generateId()); 36756 36757 this.beforeInitEvent.fire(Menu); 36758 36759 YAHOO.log("Source element: " + this.srcElement.tagName, "info", this.toString()); 36760 36761 break; 36762 36763 } 36764 36765 } 36766 else { 36767 36768 /* 36769 Note: we don't pass the user config in here yet 36770 because we only want it executed once, at the lowest 36771 subclass level. 36772 */ 36773 36774 Menu.superclass.init.call(this, p_oElement); 36775 36776 this.beforeInitEvent.fire(Menu); 36777 36778 YAHOO.log("No source element found. Created element with id: " + this.id, "info", this.toString()); 36779 36780 } 36781 36782 36783 if (this.element) { 36784 Dom.addClass(this.element, this.CSS_CLASS_NAME); 36785 36786 // Subscribe to Custom Events 36787 this.initEvent.subscribe(this._onInit); 36788 this.beforeRenderEvent.subscribe(this._onBeforeRender); 36789 this.renderEvent.subscribe(this._onRender); 36790 this.beforeShowEvent.subscribe(this._onBeforeShow); 36791 this.hideEvent.subscribe(this._onHide); 36792 this.showEvent.subscribe(this._onShow); 36793 this.beforeHideEvent.subscribe(this._onBeforeHide); 36794 this.mouseOverEvent.subscribe(this._onMouseOver); 36795 this.mouseOutEvent.subscribe(this._onMouseOut); 36796 this.clickEvent.subscribe(this._onClick); 36797 this.keyDownEvent.subscribe(this._onKeyDown); 36798 this.keyPressEvent.subscribe(this._onKeyPress); 36799 this.blurEvent.subscribe(this._onBlur); 36800 36801 if (!bFocusListenerInitialized) { 36802 Event.onFocus(document, onDocFocus); 36803 bFocusListenerInitialized = true; 36804 } 36805 36806 // Fixes an issue in Firefox 2 and Webkit where Dom's "getX" and "getY" 36807 // methods return values that don't take scrollTop into consideration 36808 36809 if ((UA.gecko && UA.gecko < 1.9) || (UA.webkit && UA.webkit < 523)) { 36810 this.cfg.subscribeToConfigEvent(_Y, this._onYChange); 36811 } 36812 36813 36814 if (p_oConfig) { 36815 this.cfg.applyConfig(p_oConfig, true); 36816 } 36817 36818 // Register the Menu instance with the MenuManager 36819 MenuManager.addMenu(this); 36820 36821 this.initEvent.fire(Menu); 36822 } 36823}, 36824 36825 36826 36827// Private methods 36828 36829 36830/** 36831* @method _initSubTree 36832* @description Iterates the childNodes of the source element to find nodes 36833* used to instantiate menu and menu items. 36834* @private 36835*/ 36836_initSubTree: function () { 36837 36838 var oSrcElement = this.srcElement, 36839 sSrcElementTagName, 36840 nGroup, 36841 sGroupTitleTagName, 36842 oNode, 36843 aListElements, 36844 nListElements, 36845 i; 36846 36847 36848 if (oSrcElement) { 36849 36850 sSrcElementTagName = 36851 (oSrcElement.tagName && oSrcElement.tagName.toUpperCase()); 36852 36853 36854 if (sSrcElementTagName == _DIV_UPPERCASE) { 36855 36856 // Populate the collection of item groups and item group titles 36857 36858 oNode = this.body.firstChild; 36859 36860 36861 if (oNode) { 36862 36863 nGroup = 0; 36864 sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase(); 36865 36866 do { 36867 36868 36869 if (oNode && oNode.tagName) { 36870 36871 switch (oNode.tagName.toUpperCase()) { 36872 36873 case sGroupTitleTagName: 36874 36875 this._aGroupTitleElements[nGroup] = oNode; 36876 36877 break; 36878 36879 case _UL_UPPERCASE: 36880 36881 this._aListElements[nGroup] = oNode; 36882 this._aItemGroups[nGroup] = []; 36883 nGroup++; 36884 36885 break; 36886 36887 } 36888 36889 } 36890 36891 } 36892 while ((oNode = oNode.nextSibling)); 36893 36894 36895 /* 36896 Apply the "first-of-type" class to the first UL to mimic 36897 the ":first-of-type" CSS3 psuedo class. 36898 */ 36899 36900 if (this._aListElements[0]) { 36901 36902 Dom.addClass(this._aListElements[0], _FIRST_OF_TYPE); 36903 36904 } 36905 36906 } 36907 36908 } 36909 36910 36911 oNode = null; 36912 36913 YAHOO.log("Searching DOM for items to initialize.", "info", this.toString()); 36914 36915 36916 if (sSrcElementTagName) { 36917 36918 switch (sSrcElementTagName) { 36919 36920 case _DIV_UPPERCASE: 36921 36922 aListElements = this._aListElements; 36923 nListElements = aListElements.length; 36924 36925 if (nListElements > 0) { 36926 36927 YAHOO.log("Found " + nListElements + " item groups to initialize.", 36928 "info", this.toString()); 36929 36930 i = nListElements - 1; 36931 36932 do { 36933 36934 oNode = aListElements[i].firstChild; 36935 36936 if (oNode) { 36937 36938 YAHOO.log("Scanning " + 36939 aListElements[i].childNodes.length + 36940 " child nodes for items to initialize.", "info", this.toString()); 36941 36942 do { 36943 36944 if (oNode && oNode.tagName && 36945 oNode.tagName.toUpperCase() == _LI) { 36946 36947 YAHOO.log("Initializing " + 36948 oNode.tagName + " node.", "info", this.toString()); 36949 36950 this.addItem(new this.ITEM_TYPE(oNode, 36951 { parent: this }), i); 36952 36953 } 36954 36955 } 36956 while ((oNode = oNode.nextSibling)); 36957 36958 } 36959 36960 } 36961 while (i--); 36962 36963 } 36964 36965 break; 36966 36967 case _SELECT: 36968 36969 YAHOO.log("Scanning " + 36970 oSrcElement.childNodes.length + 36971 " child nodes for items to initialize.", "info", this.toString()); 36972 36973 oNode = oSrcElement.firstChild; 36974 36975 do { 36976 36977 if (oNode && oNode.tagName) { 36978 36979 switch (oNode.tagName.toUpperCase()) { 36980 36981 case _OPTGROUP: 36982 case _OPTION: 36983 36984 YAHOO.log("Initializing " + 36985 oNode.tagName + " node.", "info", this.toString()); 36986 36987 this.addItem( 36988 new this.ITEM_TYPE( 36989 oNode, 36990 { parent: this } 36991 ) 36992 ); 36993 36994 break; 36995 36996 } 36997 36998 } 36999 37000 } 37001 while ((oNode = oNode.nextSibling)); 37002 37003 break; 37004 37005 } 37006 37007 } 37008 37009 } 37010 37011}, 37012 37013 37014/** 37015* @method _getFirstEnabledItem 37016* @description Returns the first enabled item in the menu.
37017* @return {YAHOO.widget.MenuItem} 37018* @private 37019*/ 37020_getFirstEnabledItem: function () { 37021 37022 var aItems = this.getItems(), 37023 nItems = aItems.length, 37024 oItem, 37025 returnVal; 37026 37027 37028 for(var i=0; i<nItems; i++) { 37029 37030 oItem = aItems[i]; 37031 37032 if (oItem && !oItem.cfg.getProperty(_DISABLED) && oItem.element.style.display != _NONE) { 37033 37034 returnVal = oItem; 37035 break; 37036 37037 } 37038 37039 } 37040 37041 return returnVal; 37042 37043}, 37044 37045 37046/** 37047* @method _addItemToGroup 37048* @description Adds a menu item to a group. 37049* @private 37050* @param {Number} p_nGroupIndex Number indicating the group to which the 37051* item belongs. 37052* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 37053* instance to be added to the menu. 37054* @param {HTML} p_oItem String or markup specifying the content of the item to be added 37055* to the menu. The item is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 37056* @param {Object} p_oItem Object literal containing a set of menu item 37057* configuration properties. 37058* @param {Number} p_nItemIndex Optional. Number indicating the index at 37059* which the menu item should be added. 37060* @return {YAHOO.widget.MenuItem} 37061*/ 37062_addItemToGroup: function (p_nGroupIndex, p_oItem, p_nItemIndex) { 37063 37064 var oItem, 37065 nGroupIndex, 37066 aGroup, 37067 oGroupItem, 37068 bAppend, 37069 oNextItemSibling, 37070 nItemIndex, 37071 returnVal; 37072 37073 37074 function getNextItemSibling(p_aArray, p_nStartIndex) { 37075 37076 return (p_aArray[p_nStartIndex] || getNextItemSibling(p_aArray, (p_nStartIndex+1))); 37077 37078 } 37079 37080 37081 if (p_oItem instanceof this.ITEM_TYPE) { 37082 37083 oItem = p_oItem; 37084 oItem.parent = this; 37085 37086 } 37087 else if (Lang.isString(p_oItem)) { 37088 37089 oItem = new this.ITEM_TYPE(p_oItem, { parent: this }); 37090 37091 } 37092 else if (Lang.isObject(p_oItem)) { 37093 37094 p_oItem.parent = this; 37095 37096 oItem = new this.ITEM_TYPE(p_oItem.text, p_oItem); 37097 37098 } 37099 37100 37101 if (oItem) { 37102 37103 if (oItem.cfg.getProperty(_SELECTED)) { 37104 37105 this.activeItem = oItem; 37106 37107 } 37108 37109 37110 nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0; 37111 aGroup = this._getItemGroup(nGroupIndex); 37112 37113 37114 37115 if (!aGroup) { 37116 37117 aGroup = this._createItemGroup(nGroupIndex); 37118 37119 } 37120 37121 37122 if (Lang.isNumber(p_nItemIndex)) { 37123 37124 bAppend = (p_nItemIndex >= aGroup.length); 37125 37126 37127 if (aGroup[p_nItemIndex]) { 37128 37129 aGroup.splice(p_nItemIndex, 0, oItem); 37130 37131 } 37132 else { 37133 37134 aGroup[p_nItemIndex] = oItem; 37135 37136 } 37137 37138 37139 oGroupItem = aGroup[p_nItemIndex]; 37140 37141 if (oGroupItem) { 37142 37143 if (bAppend && (!oGroupItem.element.parentNode || 37144 oGroupItem.element.parentNode.nodeType == 11)) { 37145 37146 this._aListElements[nGroupIndex].appendChild(oGroupItem.element); 37147 37148 } 37149 else { 37150 37151 oNextItemSibling = getNextItemSibling(aGroup, (p_nItemIndex+1)); 37152 37153 if (oNextItemSibling && (!oGroupItem.element.parentNode || 37154 oGroupItem.element.parentNode.nodeType == 11)) { 37155 37156 this._aListElements[nGroupIndex].insertBefore( 37157 oGroupItem.element, oNextItemSibling.element); 37158 37159 } 37160 37161 } 37162 37163 37164 oGroupItem.parent = this; 37165 37166 this._subscribeToItemEvents(oGroupItem); 37167 37168 this._configureSubmenu(oGroupItem); 37169 37170 this._updateItemProperties(nGroupIndex); 37171 37172 YAHOO.log("Item inserted." + 37173 " Text: " + oGroupItem.cfg.getProperty("text") + ", " + 37174 " Index: " + oGroupItem.index + ", " + 37175 " Group Index: " + oGroupItem.groupIndex, "info", this.toString()); 37176 37177 this.itemAddedEvent.fire(oGroupItem); 37178 this.changeContentEvent.fire(); 37179 37180 returnVal = oGroupItem; 37181 37182 } 37183 37184 } 37185 else { 37186 37187 nItemIndex = aGroup.length; 37188 37189 aGroup[nItemIndex] = oItem; 37190 37191 oGroupItem = aGroup[nItemIndex]; 37192 37193 37194 if (oGroupItem) { 37195 37196 if (!Dom.isAncestor(this._aListElements[nGroupIndex], oGroupItem.element)) { 37197 37198 this._aListElements[nGroupIndex].appendChild(oGroupItem.element); 37199 37200 } 37201 37202 oGroupItem.element.setAttribute(_GROUP_INDEX, nGroupIndex); 37203 oGroupItem.element.setAttribute(_INDEX, nItemIndex); 37204 37205 oGroupItem.parent = this; 37206 37207 oGroupItem.index = nItemIndex; 37208 oGroupItem.groupIndex = nGroupIndex; 37209 37210 this._subscribeToItemEvents(oGroupItem); 37211 37212 this._configureSubmenu(oGroupItem); 37213 37214 if (nItemIndex === 0) { 37215 37216 Dom.addClass(oGroupItem.element, _FIRST_OF_TYPE); 37217 37218 } 37219 37220 YAHOO.log("Item added." + 37221 " Text: " + oGroupItem.cfg.getProperty("text") + ", " + 37222 " Index: " + oGroupItem.index + ", " + 37223 " Group Index: " + oGroupItem.groupIndex, "info", this.toString()); 37224 37225 37226 this.itemAddedEvent.fire(oGroupItem); 37227 this.changeContentEvent.fire(); 37228 37229 returnVal = oGroupItem; 37230 37231 } 37232 37233 } 37234 37235 } 37236 37237 return returnVal; 37238 37239}, 37240 37241 37242/** 37243* @method _removeItemFromGroupByIndex 37244* @description Removes a menu item from a group by index. Returns the menu 37245* item that was removed. 37246* @private 37247* @param {Number} p_nGroupIndex Number indicating the group to which the menu 37248* item belongs. 37249* @param {Number} p_nItemIndex Number indicating the index of the menu item 37250* to be removed.
37251* @return {YAHOO.widget.MenuItem} 37252*/ 37253_removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) { 37254 37255 var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0, 37256 aGroup = this._getItemGroup(nGroupIndex), 37257 aArray, 37258 oItem, 37259 oUL; 37260 37261 if (aGroup) { 37262 37263 aArray = aGroup.splice(p_nItemIndex, 1); 37264 oItem = aArray[0]; 37265 37266 if (oItem) { 37267 37268 // Update the index and className properties of each member 37269 37270 this._updateItemProperties(nGroupIndex); 37271 37272 if (aGroup.length === 0) { 37273 37274 // Remove the UL 37275 37276 oUL = this._aListElements[nGroupIndex]; 37277 37278 if (oUL && oUL.parentNode) { 37279 oUL.parentNode.removeChild(oUL); 37280 } 37281 37282 // Remove the group from the array of items 37283 37284 this._aItemGroups.splice(nGroupIndex, 1); 37285 37286 37287 // Remove the UL from the array of ULs 37288 37289 this._aListElements.splice(nGroupIndex, 1); 37290 37291 37292 /* 37293 Assign the "first-of-type" class to the new first UL 37294 in the collection 37295 */ 37296 37297 oUL = this._aListElements[0]; 37298 37299 if (oUL) { 37300 37301 Dom.addClass(oUL, _FIRST_OF_TYPE); 37302 37303 } 37304 37305 } 37306 37307 37308 this.itemRemovedEvent.fire(oItem); 37309 this.changeContentEvent.fire(); 37310 37311 } 37312 37313 } 37314 37315 // Return a reference to the item that was removed 37316 37317 return oItem; 37318 37319}, 37320 37321 37322/** 37323* @method _removeItemFromGroupByValue 37324* @description Removes a menu item from a group by reference. Returns the 37325* menu item that was removed. 37326* @private 37327* @param {Number} p_nGroupIndex Number indicating the group to which the 37328* menu item belongs. 37329* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 37330* instance to be removed. 37331* @return {YAHOO.widget.MenuItem} 37332*/ 37333_removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) { 37334 37335 var aGroup = this._getItemGroup(p_nGroupIndex), 37336 nItems, 37337 nItemIndex, 37338 returnVal, 37339 i; 37340 37341 if (aGroup) { 37342 37343 nItems = aGroup.length; 37344 nItemIndex = -1; 37345 37346 if (nItems > 0) { 37347 37348 i = nItems-1; 37349 37350 do { 37351 37352 if (aGroup[i] == p_oItem) { 37353 37354 nItemIndex = i; 37355 break; 37356 37357 } 37358 37359 } 37360 while (i--); 37361 37362 if (nItemIndex > -1) { 37363 37364 returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex); 37365 37366 } 37367 37368 } 37369 37370 } 37371 37372 return returnVal; 37373 37374}, 37375 37376 37377/** 37378* @method _updateItemProperties 37379* @description Updates the "index," "groupindex," and "className" properties 37380* of the menu items in the specified group. 37381* @private 37382* @param {Number} p_nGroupIndex Number indicating the group of items to update. 37383*/ 37384_updateItemProperties: function (p_nGroupIndex) { 37385 37386 var aGroup = this._getItemGroup(p_nGroupIndex), 37387 nItems = aGroup.length, 37388 oItem, 37389 oLI, 37390 i; 37391 37392 37393 if (nItems > 0) { 37394 37395 i = nItems - 1; 37396 37397 // Update the index and className properties of each member 37398 37399 do { 37400 37401 oItem = aGroup[i]; 37402 37403 if (oItem) { 37404 37405 oLI = oItem.element; 37406 37407 oItem.index = i; 37408 oItem.groupIndex = p_nGroupIndex; 37409 37410 oLI.setAttribute(_GROUP_INDEX, p_nGroupIndex); 37411 oLI.setAttribute(_INDEX, i); 37412 37413 Dom.removeClass(oLI, _FIRST_OF_TYPE); 37414 37415 } 37416 37417 } 37418 while (i--); 37419 37420 37421 if (oLI) { 37422 37423 Dom.addClass(oLI, _FIRST_OF_TYPE); 37424 37425 } 37426 37427 } 37428 37429}, 37430 37431 37432/**
37433* @method _createItemGroup 37434* @description Creates a new menu item group (array) and its associated 37435* <code><ul></code> element. Returns an aray of menu item groups. 37436* @private 37437* @param {Number} p_nIndex Number indicating the group to create. 37438* @return {Array} 37439*/ 37440_createItemGroup: function (p_nIndex) { 37441 37442 var oUL, 37443 returnVal; 37444 37445 if (!this._aItemGroups[p_nIndex]) { 37446 37447 this._aItemGroups[p_nIndex] = []; 37448 37449 oUL = document.createElement(_UL_LOWERCASE); 37450 37451 this._aListElements[p_nIndex] = oUL; 37452 37453 returnVal = this._aItemGroups[p_nIndex]; 37454 37455 } 37456 37457 return returnVal; 37458 37459}, 37460 37461 37462/** 37463* @method _getItemGroup 37464* @description Returns the menu item group at the specified index. 37465* @private 37466* @param {Number} p_nIndex Number indicating the index of the menu item group 37467* to be retrieved. 37468* @return {Array} 37469*/ 37470_getItemGroup: function (p_nIndex) { 37471 37472 var nIndex = Lang.isNumber(p_nIndex) ? p_nIndex : 0, 37473 aGroups = this._aItemGroups, 37474 returnVal; 37475 37476 if (nIndex in aGroups) { 37477 37478 returnVal = aGroups[nIndex]; 37479 37480 } 37481 37482 return returnVal; 37483 37484}, 37485 37486 37487/**
37488* @method _configureSubmenu 37489* @description Subscribes the menu item's submenu to its parent menu's events. 37490* @private 37491* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 37492* instance with the submenu to be configured. 37493*/ 37494_configureSubmenu: function (p_oItem) { 37495 37496 var oSubmenu = p_oItem.cfg.getProperty(_SUBMENU); 37497 37498 if (oSubmenu) { 37499 37500 /* 37501 Listen for configuration changes to the parent menu 37502 so they they can be applied to the submenu. 37503 */ 37504 37505 this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange, oSubmenu, true); 37506 37507 this.renderEvent.subscribe(this._onParentMenuRender, oSubmenu, true); 37508 37509 } 37510 37511}, 37512 37513 37514 37515 37516/** 37517* @method _subscribeToItemEvents 37518* @description Subscribes a menu to a menu item's event. 37519* @private 37520* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 37521* instance whose events should be subscribed to. 37522*/ 37523_subscribeToItemEvents: function (p_oItem) { 37524 37525 p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this); 37526 p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange, p_oItem, this); 37527 37528}, 37529 37530 37531/** 37532* @method _onVisibleChange 37533* @description Change event handler for the the menu's "visible" configuration 37534* property. 37535* @private 37536* @param {String} p_sType String representing the name of the event that 37537* was fired. 37538* @param {Array} p_aArgs Array of arguments sent when the event was fired. 37539*/ 37540_onVisibleChange: function (p_sType, p_aArgs) { 37541 37542 var bVisible = p_aArgs[0]; 37543 37544 if (bVisible) { 37545 37546 Dom.addClass(this.element, _VISIBLE); 37547 37548 } 37549 else { 37550 37551 Dom.removeClass(this.element, _VISIBLE); 37552 37553 } 37554 37555}, 37556 37557 37558/**
37559* @method _cancelHideDelay 37560* @description Cancels the call to "hideMenu." 37561* @private 37562*/ 37563_cancelHideDelay: function () { 37564 37565 var oTimer = this.getRoot()._hideDelayTimer; 37566 37567 if (oTimer) { 37568 37569 oTimer.cancel(); 37570 37571 } 37572 37573}, 37574 37575 37576/** 37577* @method _execHideDelay 37578* @description Hides the menu after the number of milliseconds specified by 37579* the "hidedelay" configuration property. 37580* @private 37581*/ 37582_execHideDelay: function () { 37583 37584 this._cancelHideDelay(); 37585 37586 var oRoot = this.getRoot(); 37587 37588 oRoot._hideDelayTimer = Lang.later(oRoot.cfg.getProperty(_HIDE_DELAY), this, function () { 37589 37590 if (oRoot.activeItem) { 37591 if (oRoot.hasFocus()) { 37592 oRoot.activeItem.focus(); 37593 } 37594 oRoot.clearActiveItem(); 37595 } 37596 37597 if (oRoot == this && !(this instanceof YAHOO.widget.MenuBar) && 37598 this.cfg.getProperty(_POSITION) == _DYNAMIC) { 37599 this.hide(); 37600 } 37601 }); 37602 37603}, 37604 37605 37606/** 37607* @method _cancelShowDelay 37608* @description Cancels the call to the "showMenu." 37609* @private 37610*/ 37611_cancelShowDelay: function () { 37612 var oTimer = this.getRoot()._showDelayTimer; 37613 if (oTimer) { 37614 oTimer.cancel(); 37615 } 37616}, 37617 37618 37619/** 37620* @method _execSubmenuHideDelay 37621* @description Hides a submenu after the number of milliseconds specified by 37622* the "submenuhidedelay" configuration property have elapsed. 37623* @private 37624* @param {YAHOO.widget.Menu} p_oSubmenu Object specifying the submenu that 37625* should be hidden. 37626* @param {Number} p_nMouseX The x coordinate of the mouse when it left 37627* the specified submenu's parent menu item. 37628* @param {Number} p_nHideDelay The number of milliseconds that should ellapse 37629* before the submenu is hidden. 37630*/ 37631_execSubmenuHideDelay: function (p_oSubmenu, p_nMouseX, p_nHideDelay) { 37632 37633 p_oSubmenu._submenuHideDelayTimer = Lang.later(50, this, function () { 37634 37635 if (this._nCurrentMouseX > (p_nMouseX + 10)) { 37636 37637 p_oSubmenu._submenuHideDelayTimer = Lang.later(p_nHideDelay, p_oSubmenu, function () { 37638 37639 this.hide(); 37640 37641 }); 37642 37643 } 37644 else { 37645 37646 p_oSubmenu.hide(); 37647 37648 } 37649 37650 }); 37651 37652}, 37653 37654 37655 37656// Protected methods 37657 37658 37659/** 37660* @method _disableScrollHeader 37661* @description Disables the header used for scrolling the body of the menu. 37662* @protected 37663*/ 37664_disableScrollHeader: function () { 37665 37666 if (!this._bHeaderDisabled) { 37667 37668 Dom.addClass(this.header, _TOP_SCROLLBAR_DISABLED); 37669 this._bHeaderDisabled = true; 37670 37671 } 37672 37673}, 37674 37675 37676/** 37677* @method _disableScrollFooter 37678* @description Disables the footer used for scrolling the body of the menu. 37679* @protected 37680*/ 37681_disableScrollFooter: function () { 37682 37683 if (!this._bFooterDisabled) { 37684 37685 Dom.addClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED); 37686 this._bFooterDisabled = true; 37687 37688 } 37689 37690}, 37691 37692 37693/** 37694* @method _enableScrollHeader 37695* @description Enables the header used for scrolling the body of the menu. 37696* @protected 37697*/ 37698_enableScrollHeader: function () { 37699 37700 if (this._bHeaderDisabled) { 37701 37702 Dom.removeClass(this.header, _TOP_SCROLLBAR_DISABLED); 37703 this._bHeaderDisabled = false; 37704 37705 } 37706 37707}, 37708 37709 37710/** 37711* @method _enableScrollFooter 37712* @description Enables the footer used for scrolling the body of the menu. 37713* @protected 37714*/ 37715_enableScrollFooter: function () { 37716 37717 if (this._bFooterDisabled) { 37718 37719 Dom.removeClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED); 37720 this._bFooterDisabled = false; 37721 37722 } 37723 37724}, 37725 37726 37727/** 37728* @method _onMouseOver 37729* @description "mouseover" event handler for the menu. 37730* @protected 37731* @param {String} p_sType String representing the name of the event that 37732* was fired. 37733* @param {Array} p_aArgs Array of arguments sent when the event was fired. 37734*/ 37735_onMouseOver: function (p_sType, p_aArgs) { 37736 37737 var oEvent = p_aArgs[0], 37738 oItem = p_aArgs[1], 37739 oTarget = Event.getTarget(oEvent), 37740 oRoot = this.getRoot(), 37741 oSubmenuHideDelayTimer = this._submenuHideDelayTimer, 37742 oParentMenu, 37743 nShowDelay, 37744 bShowDelay, 37745 oActiveItem, 37746 oItemCfg, 37747 oSubmenu; 37748 37749 37750 var showSubmenu = function () { 37751 37752 if (this.parent.cfg.getProperty(_SELECTED)) { 37753 37754 this.show(); 37755 37756 } 37757 37758 }; 37759 37760 37761 if (!this._bStopMouseEventHandlers) { 37762 37763 if (!this._bHandledMouseOverEvent && (oTarget == this.element || 37764 Dom.isAncestor(this.element, oTarget))) { 37765 37766 // Menu mouseover logic 37767 37768 if (this._useHideDelay) { 37769 this._cancelHideDelay(); 37770 } 37771 37772 this._nCurrentMouseX = 0; 37773 37774 Event.on(this.element, _MOUSEMOVE, this._onMouseMove, this, true); 37775 37776 37777 /* 37778 If the mouse is moving from the submenu back to its corresponding menu item, 37779 don't hide the submenu or clear the active MenuItem. 37780 */ 37781 37782 if (!(oItem && Dom.isAncestor(oItem.element, Event.getRelatedTarget(oEvent)))) { 37783 37784 this.clearActiveItem(); 37785 37786 } 37787 37788 37789 if (this.parent && oSubmenuHideDelayTimer) { 37790 37791 oSubmenuHideDelayTimer.cancel(); 37792 37793 this.parent.cfg.setProperty(_SELECTED, true); 37794 37795 oParentMenu = this.parent.parent; 37796 37797 oParentMenu._bHandledMouseOutEvent = true; 37798 oParentMenu._bHandledMouseOverEvent = false;
37799 37800 } 37801 37802 37803 this._bHandledMouseOverEvent = true; 37804 this._bHandledMouseOutEvent = false; 37805 37806 } 37807 37808 37809 if (oItem && !oItem.handledMouseOverEvent && !oItem.cfg.getProperty(_DISABLED) && 37810 (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))) { 37811 37812 // Menu Item mouseover logic 37813 37814 nShowDelay = this.cfg.getProperty(_SHOW_DELAY); 37815 bShowDelay = (nShowDelay > 0); 37816 37817 37818 if (bShowDelay) { 37819 37820 this._cancelShowDelay(); 37821 37822 } 37823 37824 37825 oActiveItem = this.activeItem; 37826 37827 if (oActiveItem) { 37828 37829 oActiveItem.cfg.setProperty(_SELECTED, false); 37830 37831 } 37832 37833 37834 oItemCfg = oItem.cfg; 37835 37836 // Select and focus the current menu item 37837 37838 oItemCfg.setProperty(_SELECTED, true); 37839 37840 37841 if (this.hasFocus() || oRoot._hasFocus) { 37842 37843 oItem.focus(); 37844 37845 oRoot._hasFocus = false; 37846 37847 } 37848 37849 37850 if (this.cfg.getProperty(_AUTO_SUBMENU_DISPLAY)) { 37851 37852 // Show the submenu this menu item 37853 37854 oSubmenu = oItemCfg.getProperty(_SUBMENU); 37855 37856 if (oSubmenu) { 37857 37858 if (bShowDelay) { 37859 37860 oRoot._showDelayTimer = 37861 Lang.later(oRoot.cfg.getProperty(_SHOW_DELAY), oSubmenu, showSubmenu); 37862 37863 } 37864 else { 37865 37866 oSubmenu.show(); 37867 37868 } 37869 37870 } 37871 37872 } 37873 37874 oItem.handledMouseOverEvent = true; 37875 oItem.handledMouseOutEvent = false; 37876 37877 } 37878 37879 } 37880 37881}, 37882 37883 37884/** 37885* @method _onMouseOut 37886* @description "mouseout" event handler for the menu. 37887* @protected 37888* @param {String} p_sType String representing the name of the event that 37889* was fired. 37890* @param {Array} p_aArgs Array of arguments sent when the event was fired. 37891*/ 37892_onMouseOut: function (p_sType, p_aArgs) { 37893 37894 var oEvent = p_aArgs[0], 37895 oItem = p_aArgs[1], 37896 oRelatedTarget = Event.getRelatedTarget(oEvent), 37897 bMovingToSubmenu = false, 37898 oItemCfg, 37899 oSubmenu, 37900 nSubmenuHideDelay, 37901 nShowDelay; 37902 37903 37904 YAHOO.log("onMouseout: this == " + this); 37905 37906 if (!this._bStopMouseEventHandlers) { 37907 37908 if (oItem && !oItem.cfg.getProperty(_DISABLED)) { 37909 37910 oItemCfg = oItem.cfg; 37911 oSubmenu = oItemCfg.getProperty(_SUBMENU); 37912 37913 37914 if (oSubmenu && (oRelatedTarget == oSubmenu.element || Dom.isAncestor(oSubmenu.element, oRelatedTarget))) { 37915 bMovingToSubmenu = true; 37916 } 37917 37918 if (!oItem.handledMouseOutEvent && ((oRelatedTarget != oItem.element && !Dom.isAncestor(oItem.element, oRelatedTarget)) || bMovingToSubmenu)) { 37919 if (!bMovingToSubmenu) { 37920 oItem.cfg.setProperty(_SELECTED, false); 37921 if (oSubmenu) { 37922 37923 nSubmenuHideDelay = this.cfg.getProperty(_SUBMENU_HIDE_DELAY); 37924 nShowDelay = this.cfg.getProperty(_SHOW_DELAY); 37925 if (!(this instanceof YAHOO.widget.MenuBar) && nSubmenuHideDelay > 0 && nSubmenuHideDelay >= nShowDelay) { 37926 this._execSubmenuHideDelay(oSubmenu, Event.getPageX(oEvent), nSubmenuHideDelay); 37927 } else { 37928 oSubmenu.hide(); 37929 } 37930 } 37931 } 37932 37933 oItem.handledMouseOutEvent = true; 37934 oItem.handledMouseOverEvent = false; 37935 } 37936 } 37937 37938 YAHOO.log("onMouseout: oRelatedTarget = " + oRelatedTarget.className); 37939 YAHOO.log("onMouseout: this.element = " + this.element.id); 37940 YAHOO.log("onMouseout: Ancestorthis.element = " + Dom.isAncestor(this.element, oRelatedTarget)); 37941 YAHOO.log("onMouseout: canHide = " + this._didMouseLeave(o
37941RelatedTarget)); 37942 37943 if (!this._bHandledMouseOutEvent) { 37944 if (this._didMouseLeave(oRelatedTarget) || bMovingToSubmenu) { 37945 // Menu mouseout logic 37946 if (this._useHideDelay) { 37947 this._execHideDelay(); 37948 } 37949 37950 Event.removeListener(this.element, _MOUSEMOVE, this._onMouseMove); 37951 37952 this._nCurrentMouseX = Event.getPageX(oEvent); 37953 37954 this._bHandledMouseOutEvent = true; 37955 this._bHandledMouseOverEvent = false; 37956 } 37957 } 37958 } 37959 37960}, 37961 37962/** 37963 * Utilility method to determine if we really moused out of the menu based on the related target 37964 * @method _didMouseLeave 37965 * @protected 37966 * @param {HTMLElement} oRelatedTarget The related target based on which we're making the decision 37967 * @return {boolean} true if it's OK to hide based on the related target. 37968 */ 37969_didMouseLeave : function(oRelatedTarget) { 37970 // Hide if we're not moving back to the element from somewhere inside the element, or we're moving to an element inside the menu. 37971 // The shadow is treated as an edge case, inside inside the menu, but we get no further mouseouts, because it overflows the element, 37972 // so we need to close when moving to the menu. 37973 return (oRelatedTarget === this._shadow || (oRelatedTarget != this.element && !Dom.isAncestor(this.element, oRelatedTarget))); 37974}, 37975 37976/** 37977* @method _onMouseMove 37978* @description "click" event handler for the menu. 37979* @protected 37980* @param {Event} p_oEvent Object representing the DOM event object passed 37981* back by the event utility (YAHOO.util.Event). 37982* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 37983* fired the event. 37984*/ 37985_onMouseMove: function (p_oEvent, p_oMenu) { 37986 37987 if (!this._bStopMouseEventHandlers) { 37988 37989 this._nCurrentMouseX = Event.getPageX(p_oEvent); 37990 37991 } 37992 37993}, 37994 37995 37996/** 37997* @method _onClick 37998* @description "click" event handler for the menu. 37999* @protected 38000* @param {String} p_sType String representing the name of the event that 38001* was fired. 38002* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38003*/ 38004_onClick: function (p_sType, p_aArgs) { 38005 38006 var oEvent = p_aArgs[0], 38007 oItem = p_aArgs[1], 38008 bInMenuAnchor = false, 38009 oSubmenu, 38010 oMenu, 38011 oRoot, 38012 sId, 38013 sURL, 38014 nHashPos, 38015 nLen; 38016 38017 38018 var hide = function () { 38019 38020 oRoot = this.getRoot(); 38021 38022 if (oRoot instanceof YAHOO.widget.MenuBar || 38023 oRoot.cfg.getProperty(_POSITION) == _STATIC) { 38024 38025 oRoot.clearActiveItem(); 38026 38027 } 38028 else { 38029 38030 oRoot.hide(); 38031 38032 } 38033 38034 }; 38035 38036 38037 if (oItem) { 38038 38039 if (oItem.cfg.getProperty(_DISABLED)) { 38040 38041 Event.preventDefault(oEvent); 38042 38043 hide.call(this); 38044 38045 } 38046 else { 38047 38048 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 38049 38050 38051 /* 38052 Check if the URL of the anchor is pointing to an element that is 38053 a child of the menu. 38054 */ 38055 38056 sURL = oItem.cfg.getProperty(_URL); 38057 38058 38059 if (sURL) { 38060 38061 nHashPos = sURL.indexOf(_HASH); 38062 38063 nLen = sURL.length; 38064 38065 38066 if (nHashPos != -1) { 38067 38068 sURL = sURL.substr(nHashPos, nLen); 38069 38070 nLen = sURL.length; 38071 38072 38073 if (nLen > 1) { 38074 38075 sId = sURL.substr(1, nLen); 38076 38077 oMenu = YAHOO.widget.MenuManager.getMenu(sId); 38078 38079 if (oMenu) { 38080 38081 bInMenuAnchor = 38082 (this.getRoot() === oMenu.getRoot()); 38083 38084 } 38085 38086 } 38087 else if (nLen === 1) { 38088 38089 bInMenuAnchor = true; 38090 38091 } 38092 38093 } 38094 38095 } 38096 38097 38098 if (bInMenuAnchor && !oItem.cfg.getProperty(_TARGET)) { 38099 38100 Event.preventDefault(oEvent); 38101 38102 38103 if (UA.webkit) { 38104 38105 oItem.focus(); 38106 38107 } 38108 else { 38109 38110 oItem.focusEvent.fire(); 38111 38112 } 38113 38114 } 38115 38116 38117 if (!oSubmenu && !this.cfg.getProperty(_KEEP_OPEN)) { 38118 38119 hide.call(this); 38120 38121 } 38122 38123 } 38124 38125 } 38126 38127}, 38128 38129/* 38130 This function is called to prevent a bug in Firefox. In Firefox, 38131 moving a DOM element into a stationary mouse pointer will cause the 38132 browser to fire mouse events. This can result in the menu mouse 38133 event handlers being called uncessarily, especially when menus are 38134 moved into a stationary mouse pointer as a result of a 38135 key event handler. 38136*/ 38137/** 38138 * Utility method to stop mouseevents from being fired if the DOM 38139 * changes under a stationary mouse pointer (as opposed to the mouse moving 38140 * over a DOM element). 38141 * 38142 * @method _stopMouseEventHandlers 38143 * @private 38144 */ 38145_stopMouseEventHandlers: function() { 38146 this._bStopMouseEventHandlers = true; 38147 38148 Lang.later(10, this, function () { 38149 this._bStopMouseEventHandlers = false;
38150 }); 38151}, 38152 38153/** 38154* @method _onKeyDown 38155* @description "keydown" event handler for the menu. 38156* @protected 38157* @param {String} p_sType String representing the name of the event that 38158* was fired. 38159* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38160*/ 38161_onKeyDown: function (p_sType, p_aArgs) { 38162 38163 var oEvent = p_aArgs[0], 38164 oItem = p_aArgs[1], 38165 oSubmenu, 38166 oItemCfg, 38167 oParentItem, 38168 oRoot, 38169 oNextItem, 38170 oBody, 38171 nBodyScrollTop, 38172 nBodyOffsetHeight, 38173 aItems, 38174 nItems, 38175 nNextItemOffsetTop, 38176 nScrollTarget, 38177 oParentMenu, 38178 oFocusedEl; 38179 38180 38181 if (this._useHideDelay) { 38182 this._cancelHideDelay(); 38183 } 38184 38185 if (oItem && !oItem.cfg.getProperty(_DISABLED)) { 38186 38187 oItemCfg = oItem.cfg; 38188 oParentItem = this.parent; 38189 38190 switch(oEvent.keyCode) { 38191 38192 case 38: // Up arrow 38193 case 40: // Down arrow 38194 38195 oNextItem = (oEvent.keyCode == 38) ? 38196 oItem.getPreviousEnabledSibling() : 38197 oItem.getNextEnabledSibling(); 38198 38199 if (oNextItem) { 38200 38201 this.clearActiveItem(); 38202 38203 oNextItem.cfg.setProperty(_SELECTED, true); 38204 oNextItem.focus(); 38205 38206 if (this.cfg.getProperty(_MAX_HEIGHT) > 0 || Dom.hasClass(this.body, _YUI_MENU_BODY_SCROLLED)) { 38207 38208 oBody = this.body; 38209 nBodyScrollTop = oBody.scrollTop; 38210 nBodyOffsetHeight = oBody.offsetHeight; 38211 aItems = this.getItems(); 38212 nItems = aItems.length - 1; 38213 nNextItemOffsetTop = oNextItem.element.offsetTop; 38214 38215 38216 if (oEvent.keyCode == 40 ) { // Down 38217 38218 if (nNextItemOffsetTop >= (nBodyOffsetHeight + nBodyScrollTop)) { 38219 38220 oBody.scrollTop = nNextItemOffsetTop - nBodyOffsetHeight; 38221 38222 } 38223 else if (nNextItemOffsetTop <= nBodyScrollTop) { 38224 38225 oBody.scrollTop = 0; 38226 38227 } 38228 38229 38230 if (oNextItem == aItems[nItems]) { 38231 38232 oBody.scrollTop = oNextItem.element.offsetTop; 38233 38234 } 38235 38236 } 38237 else { // Up 38238 38239 if (nNextItemOffsetTop <= nBodyScrollTop) { 38240 38241 oBody.scrollTop = nNextItemOffsetTop - oNextItem.element.offsetHeight; 38242 38243 } 38244 else if (nNextItemOffsetTop >= (nBodyScrollTop + nBodyOffsetHeight)) { 38245 38246 oBody.scrollTop = nNextItemOffsetTop; 38247 38248 } 38249 38250 38251 if (oNextItem == aItems[0]) { 38252 38253 oBody.scrollTop = 0; 38254 38255 } 38256 38257 } 38258 38259 38260 nBodyScrollTop = oBody.scrollTop; 38261 nScrollTarget = oBody.scrollHeight - oBody.offsetHeight; 38262 38263 if (nBodyScrollTop === 0) { 38264 38265 this._disableScrollHeader(); 38266 this._enableScrollFooter(); 38267 38268 } 38269 else if (nBodyScrollTop == nScrollTarget) { 38270 38271 this._enableScrollHeader(); 38272 this._disableScrollFooter(); 38273 38274 } 38275 else { 38276 38277 this._enableScrollHeader(); 38278 this._enableScrollFooter(); 38279 38280 } 38281 38282 } 38283 38284 } 38285 38286 38287 Event.preventDefault(oEvent); 38288 38289 this._stopMouseEventHandlers(); 38290 38291 break; 38292 38293 38294 case 39: // Right arrow 38295 38296 oSubmenu = oItemCfg.getProperty(_SUBMENU); 38297 38298 if (oSubmenu) { 38299
38300 if (!oItemCfg.getProperty(_SELECTED)) { 38301 38302 oItemCfg.setProperty(_SELECTED, true); 38303 38304 } 38305 38306 oSubmenu.show(); 38307 oSubmenu.setInitialFocus(); 38308 oSubmenu.setInitialSelection(); 38309 38310 } 38311 else { 38312 38313 oRoot = this.getRoot(); 38314 38315 if (oRoot instanceof YAHOO.widget.MenuBar) { 38316 38317 oNextItem = oRoot.activeItem.getNextEnabledSibling(); 38318 38319 if (oNextItem) { 38320 38321 oRoot.clearActiveItem(); 38322 38323 oNextItem.cfg.setProperty(_SELECTED, true); 38324 38325 oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); 38326 38327 if (oSubmenu) { 38328 38329 oSubmenu.show(); 38330 oSubmenu.setInitialFocus(); 38331 38332 } 38333 else { 38334 38335 oNextItem.focus(); 38336 38337 } 38338 38339 } 38340 38341 } 38342 38343 } 38344 38345 38346 Event.preventDefault(oEvent); 38347 38348 this._stopMouseEventHandlers(); 38349 38350 break; 38351 38352 38353 case 37: // Left arrow 38354 38355 if (oParentItem) { 38356 38357 oParentMenu = oParentItem.parent; 38358 38359 if (oParentMenu instanceof YAHOO.widget.MenuBar) { 38360 38361 oNextItem = 38362 oParentMenu.activeItem.getPreviousEnabledSibling(); 38363 38364 if (oNextItem) { 38365 38366 oParentMenu.clearActiveItem(); 38367 38368 oNextItem.cfg.setProperty(_SELECTED, true); 38369 38370 oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); 38371 38372 if (oSubmenu) { 38373 38374 oSubmenu.show(); 38375 oSubmenu.setInitialFocus(); 38376 38377 } 38378 else { 38379 38380 oNextItem.focus(); 38381 38382 } 38383 38384 } 38385 38386 } 38387 else { 38388 38389 this.hide(); 38390 38391 oParentItem.focus(); 38392 38393 } 38394 38395 } 38396 38397 Event.preventDefault(oEvent); 38398 38399 this._stopMouseEventHandlers(); 38400 38401 break; 38402 38403 } 38404 38405 38406 } 38407 38408 38409 if (oEvent.keyCode == 27) { // Esc key 38410 38411 if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 38412 38413 this.hide(); 38414 38415 if (this.parent) { 38416 38417 this.parent.focus(); 38418 38419 } 38420 else { 38421 // Focus the element that previously had focus 38422 38423 oFocusedEl = this._focusedElement; 38424 38425 if (oFocusedEl && oFocusedEl.focus) { 38426 38427 try { 38428 oFocusedEl.focus(); 38429 } 38430 catch(ex) { 38431 } 38432 38433 } 38434 38435 } 38436 38437 } 38438 else if (this.activeItem) { 38439 38440 oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU); 38441 38442 if (oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) { 38443 38444 oSubmenu.hide(); 38445 this.activeItem.focus(); 38446 38447 } 38448 else { 38449 38450 this.activeItem.blur(); 38451 this.activeItem.cfg.setProperty(_SELECTED, false); 38452 38453 } 38454 38455 } 38456 38457 38458 Event.preventDefault(oEvent); 38459 38460 } 38461 38462}, 38463 38464 38465/** 38466* @method _onKeyPress 38467* @description "keypress" event handler for a Menu instance. 38468* @protected 38469* @param {String} p_sType The name of the event that was fired. 38470* @param {Array} p_aArgs Collection of arguments sent when the event 38471* was fired. 38472*/ 38473_onKeyPress: function (p_sType, p_aArgs) { 38474
38475 var oEvent = p_aArgs[0]; 38476 38477 38478 if (oEvent.keyCode == 40 || oEvent.keyCode == 38) { 38479 38480 Event.preventDefault(oEvent); 38481 38482 } 38483 38484}, 38485 38486 38487/** 38488* @method _onBlur 38489* @description "blur" event handler for a Menu instance. 38490* @protected 38491* @param {String} p_sType The name of the event that was fired. 38492* @param {Array} p_aArgs Collection of arguments sent when the event 38493* was fired. 38494*/ 38495_onBlur: function (p_sType, p_aArgs) { 38496 38497 if (this._hasFocus) { 38498 this._hasFocus = false; 38499 } 38500 38501}, 38502 38503/** 38504* @method _onYChange 38505* @description "y" event handler for a Menu instance. 38506* @protected 38507* @param {String} p_sType The name of the event that was fired. 38508* @param {Array} p_aArgs Collection of arguments sent when the event 38509* was fired. 38510*/ 38511_onYChange: function (p_sType, p_aArgs) { 38512 38513 var oParent = this.parent, 38514 nScrollTop, 38515 oIFrame, 38516 nY; 38517 38518 38519 if (oParent) { 38520 38521 nScrollTop = oParent.parent.body.scrollTop; 38522 38523 38524 if (nScrollTop > 0) { 38525 38526 nY = (this.cfg.getProperty(_Y) - nScrollTop); 38527 38528 Dom.setY(this.element, nY); 38529 38530 oIFrame = this.iframe; 38531 38532 38533 if (oIFrame) { 38534 38535 Dom.setY(oIFrame, nY); 38536 38537 } 38538 38539 this.cfg.setProperty(_Y, nY, true); 38540 38541 } 38542 38543 } 38544 38545}, 38546 38547 38548/** 38549* @method _onScrollTargetMouseOver 38550* @description "mouseover" event handler for the menu's "header" and "footer" 38551* elements. Used to scroll the body of the menu up and down when the 38552* menu's "maxheight" configuration property is set to a value greater than 0. 38553* @protected 38554* @param {Event} p_oEvent Object representing the DOM event object passed 38555* back by the event utility (YAHOO.util.Event). 38556* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 38557* fired the event. 38558*/ 38559_onScrollTargetMouseOver: function (p_oEvent, p_oMenu) { 38560 38561 var oBodyScrollTimer = this._bodyScrollTimer; 38562 38563 38564 if (oBodyScrollTimer) { 38565 38566 oBodyScrollTimer.cancel(); 38567 38568 } 38569 38570 38571 this._cancelHideDelay(); 38572 38573 38574 var oTarget = Event.getTarget(p_oEvent), 38575 oBody = this.body, 38576 nScrollIncrement = this.cfg.getProperty(_SCROLL_INCREMENT), 38577 nScrollTarget, 38578 fnScrollFunction; 38579 38580 38581 function scrollBodyDown() { 38582 38583 var nScrollTop = oBody.scrollTop; 38584 38585 38586 if (nScrollTop < nScrollTarget) { 38587 38588 oBody.scrollTop = (nScrollTop + nScrollIncrement); 38589 38590 this._enableScrollHeader(); 38591 38592 } 38593 else { 38594 38595 oBody.scrollTop = nScrollTarget; 38596 38597 this._bodyScrollTimer.cancel(); 38598 38599 this._disableScrollFooter(); 38600 38601 } 38602 38603 } 38604 38605 38606 function scrollBodyUp() { 38607 38608 var nScrollTop = oBody.scrollTop; 38609 38610 38611 if (nScrollTop > 0) { 38612 38613 oBody.scrollTop = (nScrollTop - nScrollIncrement); 38614 38615 this._enableScrollFooter(); 38616 38617 } 38618 else { 38619 38620 oBody.scrollTop = 0; 38621 38622 this._bodyScrollTimer.cancel(); 38623 38624 this._disableScrollHeader(); 38625 38626 } 38627 38628 } 38629 38630 38631 if (Dom.hasClass(oTarget, _HD)) { 38632 38633 fnScrollFunction = scrollBodyUp; 38634 38635 } 38636 else { 38637 38638 nScrollTarget = oBody.scrollHeight - oBody.offsetHeight; 38639 38640 fnScrollFunction = scrollBodyDown; 38641 38642 } 38643 38644 38645 this._bodyScrollTimer = Lang.later(10, this, fnScrollFunction, null, true); 38646 38647}, 38648 38649 38650/** 38651* @method _onScrollTargetMouseOut 38652* @description "mouseout" event handler for the menu's "header" and "footer" 38653* elements. Used to stop scrolling the body of the menu up and down when the 38654* menu's "maxheight" configuration property is set to a value greater than 0. 38655* @protected 38656* @param {Event} p_oEvent Object representing the DOM event object passed 38657* back by the event utility (YAHOO.util.Event). 38658* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 38659* fired the event. 38660*/ 38661_onScrollTargetMouseOut: function (p_oEvent, p_oMenu) { 38662 38663 var oBodyScrollTimer = this._bodyScrollTimer; 38664 38665 if (oBodyScrollTimer) { 38666 38667 oBodyScrollTimer.cancel(); 38668 38669 } 38670 38671 this._cancelHideDelay(); 38672 38673}, 38674 38675 38676 38677// Private methods 38678 38679 38680/** 38681* @method _onInit 38682* @description "init" event handler for the menu. 38683* @private 38684* @param {String}
38684 p_sType String representing the name of the event that 38685* was fired. 38686* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38687*/ 38688_onInit: function (p_sType, p_aArgs) { 38689 38690 this.cfg.subscribeToConfigEvent(_VISIBLE, this._onVisibleChange); 38691 38692 var bRootMenu = !this.parent, 38693 bLazyLoad = this.lazyLoad; 38694 38695 38696 /* 38697 Automatically initialize a menu's subtree if: 38698 38699 1) This is the root menu and lazyload is off 38700 38701 2) This is the root menu, lazyload is on, but the menu is 38702 already visible 38703 38704 3) This menu is a submenu and lazyload is off 38705 */ 38706 38707 38708 38709 if (((bRootMenu && !bLazyLoad) || 38710 (bRootMenu && (this.cfg.getProperty(_VISIBLE) || 38711 this.cfg.getProperty(_POSITION) == _STATIC)) || 38712 (!bRootMenu && !bLazyLoad)) && this.getItemGroups().length === 0) { 38713 38714 if (this.srcElement) { 38715 38716 this._initSubTree(); 38717 38718 } 38719 38720 38721 if (this.itemData) { 38722 38723 this.addItems(this.itemData); 38724 38725 } 38726 38727 } 38728 else if (bLazyLoad) { 38729 38730 this.cfg.fireQueue(); 38731 38732 } 38733 38734}, 38735 38736 38737/** 38738* @method _onBeforeRender 38739* @description "beforerender" event handler for the menu. Appends all of the 38740* <code><ul></code>, <code><li></code> and their accompanying 38741* title elements to the body element of the menu. 38742* @private 38743* @param {String} p_sType String representing the name of the event that 38744* was fired. 38745* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38746*/ 38747_onBeforeRender: function (p_sType, p_aArgs) { 38748 38749 var oEl = this.element, 38750 nListElements = this._aListElements.length, 38751 bFirstList = true, 38752 i = 0, 38753 oUL, 38754 oGroupTitle; 38755 38756 if (nListElements > 0) { 38757 38758 do { 38759 38760 oUL = this._aListElements[i]; 38761 38762 if (oUL) { 38763 38764 if (bFirstList) { 38765 38766 Dom.addClass(oUL, _FIRST_OF_TYPE); 38767 bFirstList = false; 38768 38769 } 38770 38771 38772 if (!Dom.isAncestor(oEl, oUL)) { 38773 38774 this.appendToBody(oUL); 38775 38776 } 38777 38778 38779 oGroupTitle = this._aGroupTitleElements[i]; 38780 38781 if (oGroupTitle) { 38782 38783 if (!Dom.isAncestor(oEl, oGroupTitle)) { 38784 38785 oUL.parentNode.insertBefore(oGroupTitle, oUL); 38786 38787 } 38788 38789 38790 Dom.addClass(oUL, _HAS_TITLE); 38791 38792 } 38793 38794 } 38795 38796 i++; 38797 38798 } 38799 while (i < nListElements); 38800 38801 } 38802 38803}, 38804 38805 38806/** 38807* @method _onRender 38808* @description "render" event handler for the menu. 38809* @private 38810* @param {String} p_sType String representing the name of the event that 38811* was fired. 38812* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38813*/ 38814_onRender: function (p_sType, p_aArgs) { 38815 38816 if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 38817 38818 if (!this.cfg.getProperty(_VISIBLE)) { 38819 38820 this.positionOffScreen(); 38821 38822 } 38823 38824 } 38825 38826}, 38827 38828 38829 38830 38831 38832/** 38833* @method _onBeforeShow 38834* @description "beforeshow" event handler for the menu. 38835* @private 38836* @param {String} p_sType String representing the name of the event that 38837* was fired. 38838* @param {Array} p_aArgs Array of arguments sent when the event was fired. 38839*/ 38840_onBeforeShow: function (p_sType, p_aArgs) { 38841 38842 var nOptions, 38843 n, 38844 oSrcElement, 38845 oContainer = this.cfg.getProperty(_CONTAINER); 38846 38847 38848 if (this.lazyLoad && this.getItemGroups().length === 0) { 38849 38850 if (this.srcElement) { 38851 38852 this._initSubTree(); 38853 38854 } 38855 38856 38857 if (this.itemData) { 38858 38859 if (this.parent && this.parent.parent && 38860 this.parent.parent.srcElement && 38861 this.parent.parent.srcElement.tagName.toUpperCase() == 38862 _SELECT) { 38863 38864 nOptions = this.itemData.length; 38865 38866 for(n=0; n<nOptions; n++) { 38867 38868 if (this.itemData[n].tagName) { 38869 38870 this.addItem((new this.ITEM_TYPE(this.itemData[n]))); 38871 38872 } 38873 38874 } 38875 38876 } 38877 else { 38878 38879 this.addItems(this.itemData); 38880 38881 } 38882 38883 } 38884 38885 38886 oSrcElement = this.srcElement; 38887 38888 if (oSrcElement) { 38889 38890 if (oSrcElement.tagName.toUpperCase() == _SELECT) { 38891 38892 if (Dom.inDocument(oSrcElement)) { 38893 38894 this.render(oSrcElement.parentNode); 38895 38896 } 38897 else { 38898 38899 this.render(oContainer); 38900 38901 } 38902 38903 } 38904 else { 38905 38906 this.render(); 38907 38908 } 38909 38910 } 38911 else { 38912 38913 if (this.parent) { 38914 38915 this.render(this.parent.element); 38916 38917 } 38918 else { 38919 38920 this.render(oContainer); 38921 38922 } 38923 38924 } 38925 38926 } 38927 38928 38929 38930 var oParent = this.parent, 38931 aAlignment; 38932 38933 38934 if (!oParent && this.cfg.getProperty(_POSITION) == _DYNAMIC) { 38935 38936 this.cfg.refireEvent(_XY); 38937 38938 } 38939 38940 38941 if (oParent) { 38942 38943 aAlignment = oParent.parent.cfg.getProperty(_SUBMENU_ALIGNMENT); 38944 38945 this.cfg.setProperty(_CONTEXT, [oParent.element, aAlignment[0], aAlignment[1]]); 38946 this.align(); 38947 38948 } 38949 38950}, 38951 38952 38953getConstrainedY: function (y) { 38954 38955 var oMenu = this, 38956 38957 aContext = oMenu.cfg.getProperty(_CONTEXT), 38958 nInitialMaxHeight = oMenu.cfg.getProperty(_MAX_HEIGHT), 38959 38960 nMaxHeight, 38961 38962 oOverlapPositions = { 38963 38964 "trbr": true, 38965 "tlbl": true, 38966 "bltl": true, 38967 "brtr": true 38968 38969 }, 38970 38971 bPotentialContextOverlap = (aContext && oOverlapPositions[aContext[1] + aContext[2]]), 38972 38973 oMenuEl = oMenu.element, 38974 nMenuOffsetHeight = oMenuEl.offsetHeight, 38975 38976 nViewportOffset = Overlay.VIEWPORT_OFFSET,
38977 viewPortHeight = Dom.getViewportHeight(), 38978 scrollY = Dom.getDocumentScrollTop(), 38979 38980 bCanConstrain = 38981 (oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) + nViewportOffset < viewPortHeight), 38982 38983 nAvailableHeight, 38984 38985 oContextEl, 38986 nContextElY, 38987 nContextElHeight, 38988 38989 bFlipped = false, 38990 38991 nTopRegionHeight, 38992 nBottomRegionHeight, 38993 38994 topConstraint = scrollY + nViewportOffset, 38995 bottomConstraint = scrollY + viewPortHeight - nMenuOffsetHeight - nViewportOffset, 38996 38997 yNew = y; 38998 38999 39000 var flipVertical = function () { 39001 39002 var nNewY; 39003 39004 // The Menu is below the context element, flip it above 39005 if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 39006 nNewY = (nContextElY - nMenuOffsetHeight); 39007 } 39008 else { // The Menu is above the context element, flip it below 39009 nNewY = (nContextElY + nContextElHeight); 39010 } 39011 39012 oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true); 39013 39014 return nNewY; 39015 39016 }; 39017 39018 39019 /* 39020 Uses the context element's position to calculate the availble height 39021 above and below it to display its corresponding Menu. 39022 */ 39023 39024 var getDisplayRegionHeight = function () { 39025 39026 // The Menu is below the context element 39027 if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 39028 return (nBottomRegionHeight - nViewportOffset); 39029 } 39030 else { // The Menu is above the context element 39031 return (nTopRegionHeight - nViewportOffset); 39032 } 39033 39034 }; 39035 39036 39037 /* 39038 Sets the Menu's "y" configuration property to the correct value based on its 39039 current orientation. 39040 */ 39041 39042 var alignY = function () { 39043 39044 var nNewY; 39045 39046 if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 39047 nNewY = (nContextElY + nContextElHeight); 39048 } 39049 else { 39050 nNewY = (nContextElY - oMenuEl.offsetHeight); 39051 } 39052 39053 oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true); 39054 39055 }; 39056 39057 39058 // Resets the maxheight of the Menu to the value set by the user 39059 39060 var resetMaxHeight = function () { 39061 39062 oMenu._setScrollHeight(this.cfg.getProperty(_MAX_HEIGHT)); 39063 39064 oMenu.hideEvent.unsubscribe(resetMaxHeight); 39065 39066 }; 39067 39068 39069 /* 39070 Trys to place the Menu in the best possible position (either above or 39071 below its corresponding context element). 39072 */ 39073 39074 var setVerticalPosition = function () { 39075 39076 var nDisplayRegionHeight = getDisplayRegionHeight(), 39077 bMenuHasItems = (oMenu.getItems().length > 0), 39078 nMenuMinScrollHeight, 39079 fnReturnVal; 39080 39081 39082 if (nMenuOffsetHeight > nDisplayRegionHeight) { 39083 39084 nMenuMinScrollHeight = 39085 bMenuHasItems ? oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) : nMenuOffsetHeight; 39086 39087 39088 if ((nDisplayRegionHeight > nMenuMinScrollHeight) && bMenuHasItems) { 39089 nMaxHeight = nDisplayRegionHeight; 39090 } 39091 else { 39092 nMaxHeight = nInitialMaxHeight; 39093 } 39094 39095 39096 oMenu._setScrollHeight(nMaxHeight); 39097 oMenu.hideEvent.subscribe(resetMaxHeight); 39098 39099 39100 // Re-align the Menu since its height has just changed 39101 // as a result of the setting of the maxheight property. 39102 39103 alignY(); 39104 39105 39106 if (nDisplayRegionHeight < nMenuMinScrollHeight) { 39107 39108 if (bFlipped) { 39109 39110 /* 39111 All possible positions and values for the "maxheight" 39112 configuration property have been tried, but none were 39113 successful, so fall back to the original size and position. 39114 */ 39115 39116 flipVertical(); 39117 39118 } 39119 else { 39120 39121 flipVertical(); 39122 39123 bFlipped = true; 39124 39125 fnReturnVal = setVerticalPosition(); 39126 39127 } 39128 39129 } 39130 39131 } 39132 else if (nMaxHeight && (nMaxHeight !== nInitialMaxHeight)) { 39133 39134 oMenu._setScrollHeight(nInitialMaxHeight);
39135 oMenu.hideEvent.subscribe(resetMaxHeight); 39136 39137 // Re-align the Menu since its height has just changed 39138 // as a result of the setting of the maxheight property. 39139 39140 alignY(); 39141 39142 } 39143 39144 return fnReturnVal; 39145 39146 }; 39147 39148 39149 // Determine if the current value for the Menu's "y" configuration property will 39150 // result in the Menu being positioned outside the boundaries of the viewport 39151 39152 if (y < topConstraint || y > bottomConstraint) { 39153 39154 // The current value for the Menu's "y" configuration property WILL 39155 // result in the Menu being positioned outside the boundaries of the viewport 39156 39157 if (bCanConstrain) { 39158 39159 if (oMenu.cfg.getProperty(_PREVENT_CONTEXT_OVERLAP) && bPotentialContextOverlap) { 39160 39161 // SOLUTION #1: 39162 // If the "preventcontextoverlap" configuration property is set to "true", 39163 // try to flip and/or scroll the Menu to both keep it inside the boundaries of the 39164 // viewport AND from overlaping its context element (MenuItem or MenuBarItem). 39165 39166 oContextEl = aContext[0]; 39167 nContextElHeight = oContextEl.offsetHeight; 39168 nContextElY = (Dom.getY(oContextEl) - scrollY); 39169 39170 nTopRegionHeight = nContextElY; 39171 nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight)); 39172 39173 setVerticalPosition(); 39174 39175 yNew = oMenu.cfg.getProperty(_Y); 39176 39177 } 39178 else if (!(oMenu instanceof YAHOO.widget.MenuBar) && 39179 nMenuOffsetHeight >= viewPortHeight) { 39180 39181 // SOLUTION #2: 39182 // If the Menu exceeds the height of the viewport, introduce scroll bars 39183 // to keep the Menu inside the boundaries of the viewport 39184 39185 nAvailableHeight = (viewPortHeight - (nViewportOffset * 2)); 39186 39187 if (nAvailableHeight > oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT)) { 39188 39189 oMenu._setScrollHeight(nAvailableHeight); 39190 oMenu.hideEvent.subscribe(resetMaxHeight); 39191 39192 alignY(); 39193 39194 yNew = oMenu.cfg.getProperty(_Y); 39195 39196 } 39197 39198 } 39199 else { 39200 39201 // SOLUTION #3: 39202 39203 if (y < topConstraint) { 39204 yNew = topConstraint; 39205 } else if (y > bottomConstraint) { 39206 yNew = bottomConstraint; 39207 } 39208 39209 } 39210 39211 } 39212 else { 39213 // The "y" configuration property cannot be set to a value that will keep 39214 // entire Menu inside the boundary of the viewport. Therefore, set 39215 // the "y" configuration property to scrollY to keep as much of the 39216 // Menu inside the viewport as possible. 39217 yNew = nViewportOffset + scrollY; 39218 } 39219 39220 } 39221 39222 return yNew; 39223 39224}, 39225 39226 39227/** 39228* @method _onHide 39229* @description "hide" event handler for the menu. 39230* @private 39231* @param {String} p_sType String representing the name of the event that 39232* was fired. 39233* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39234*/ 39235_onHide: function (p_sType, p_aArgs) { 39236 39237 if (this.cfg.getProperty(_POSITION) === _DYNAMIC) { 39238 39239 this.positionOffScreen(); 39240 39241 } 39242 39243}, 39244 39245 39246/** 39247* @method _onShow 39248* @description "show" event handler for the menu. 39249* @private 39250* @param {String} p_sType String representing the name of the event that 39251* was fired. 39252* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39253*/ 39254_onShow: function (p_sType, p_aArgs) { 39255 39256 var oParent = this.parent, 39257 oParentMenu, 39258 oElement, 39259 nOffsetWidth, 39260 sWidth; 39261 39262 39263 function disableAutoSubmenuDisplay(p_oEvent) { 39264 39265 var oTarget; 39266 39267 if (p_oEvent.type == _MOUSEDOWN || (p_oEvent.type == _KEYDOWN && p_oEvent.keyCode == 27)) { 39268 39269 /*
39270 Set the "autosubmenudisplay" to "false" if the user 39271 clicks outside the menu bar. 39272 */ 39273 39274 oTarget = Event.getTarget(p_oEvent); 39275 39276 if (oTarget != oParentMenu.element || !Dom.isAncestor(oParentMenu.element, oTarget)) { 39277 39278 oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false); 39279 39280 Event.removeListener(document, _MOUSEDOWN, disableAutoSubmenuDisplay); 39281 Event.removeListener(document, _KEYDOWN, disableAutoSubmenuDisplay); 39282 39283 } 39284 39285 } 39286 39287 } 39288 39289 39290 function onSubmenuHide(p_sType, p_aArgs, p_sWidth) { 39291 39292 this.cfg.setProperty(_WIDTH, _EMPTY_STRING); 39293 this.hideEvent.unsubscribe(onSubmenuHide, p_sWidth); 39294 39295 } 39296 39297 39298 if (oParent) { 39299 39300 oParentMenu = oParent.parent; 39301 39302 39303 if (!oParentMenu.cfg.getProperty(_AUTO_SUBMENU_DISPLAY) && 39304 (oParentMenu instanceof YAHOO.widget.MenuBar || 39305 oParentMenu.cfg.getProperty(_POSITION) == _STATIC)) { 39306 39307 oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, true); 39308 39309 Event.on(document, _MOUSEDOWN, disableAutoSubmenuDisplay); 39310 Event.on(document, _KEYDOWN, disableAutoSubmenuDisplay); 39311 39312 } 39313 39314 39315 // The following fixes an issue with the selected state of a MenuItem 39316 // not rendering correctly when a submenu is aligned to the left of 39317 // its parent Menu instance. 39318 39319 if ((this.cfg.getProperty("x") < oParentMenu.cfg.getProperty("x")) && 39320 (UA.gecko && UA.gecko < 1.9) && !this.cfg.getProperty(_WIDTH)) { 39321 39322 oElement = this.element; 39323 nOffsetWidth = oElement.offsetWidth; 39324 39325 /* 39326 Measuring the difference of the offsetWidth before and after 39327 setting the "width" style attribute allows us to compute the 39328 about of padding and borders applied to the element, which in 39329 turn allows us to set the "width" property correctly. 39330 */ 39331 39332 oElement.style.width = nOffsetWidth + _PX; 39333 39334 sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX; 39335 39336 this.cfg.setProperty(_WIDTH, sWidth); 39337 39338 this.hideEvent.subscribe(onSubmenuHide, sWidth); 39339 39340 } 39341 39342 } 39343 39344 39345 /* 39346 Dynamically positioned, root Menus focus themselves when visible, and 39347 will then, when hidden, restore focus to the UI control that had focus 39348 before the Menu was made visible. 39349 */ 39350 39351 if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) { 39352 39353 this._focusedElement = oFocusedElement; 39354 39355 this.focus(); 39356 39357 } 39358 39359 39360}, 39361 39362 39363/** 39364* @method _onBeforeHide 39365* @description "beforehide" event handler for the menu. 39366* @private 39367* @param {String} p_sType String representing the name of the event that 39368* was fired. 39369* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39370*/ 39371_onBeforeHide: function (p_sType, p_aArgs) { 39372 39373 var oActiveItem = this.activeItem, 39374 oRoot = this.getRoot(), 39375 oConfig, 39376 oSubmenu; 39377 39378 39379 if (oActiveItem) { 39380 39381 oConfig = oActiveItem.cfg; 39382 39383 oConfig.setProperty(_SELECTED, false); 39384 39385 oSubmenu = oConfig.getProperty(_SUBMENU); 39386 39387 if (oSubmenu) { 39388 39389 oSubmenu.hide(); 39390 39391 } 39392 39393 } 39394 39395 39396 /* 39397 Focus can get lost in IE when the mouse is moving from a submenu back to its parent Menu. 39398 For this reason, it is necessary to maintain the focused state in a private property 39399 so that the _onMouseOver event handler is able to determined whether or not to set focus 39400 to MenuItems as the user is moving the mouse. 39401 */ 39402 39403 if (UA.ie && this.cfg.getProperty(_POSITION) === _DYNAMIC && this.parent) { 39404 39405 oRoot._hasFocus = this.hasFocus(); 39406 39407 } 39408 39409 39410 if (oRoot == this) { 39411 39412 oRoot.blur(); 39413 39414 } 39415 39416}, 39417 39418 39419/** 39420* @method _onParentMenuConfigChange 39421* @description "configchange" event handler for a submenu. 39422* @private 39423* @param {String}
39423 p_sType String representing the name of the event that 39424* was fired. 39425* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39426* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 39427* subscribed to the event. 39428*/ 39429_onParentMenuConfigChange: function (p_sType, p_aArgs, p_oSubmenu) { 39430 39431 var sPropertyName = p_aArgs[0][0], 39432 oPropertyValue = p_aArgs[0][1]; 39433 39434 switch(sPropertyName) { 39435 39436 case _IFRAME: 39437 case _CONSTRAIN_TO_VIEWPORT: 39438 case _HIDE_DELAY: 39439 case _SHOW_DELAY: 39440 case _SUBMENU_HIDE_DELAY: 39441 case _CLICK_TO_HIDE: 39442 case _EFFECT: 39443 case _CLASSNAME: 39444 case _SCROLL_INCREMENT: 39445 case _MAX_HEIGHT: 39446 case _MIN_SCROLL_HEIGHT: 39447 case _MONITOR_RESIZE: 39448 case _SHADOW: 39449 case _PREVENT_CONTEXT_OVERLAP: 39450 case _KEEP_OPEN: 39451 39452 p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue); 39453 39454 break; 39455 39456 case _SUBMENU_ALIGNMENT: 39457 39458 if (!(this.parent.parent instanceof YAHOO.widget.MenuBar)) { 39459 39460 p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue); 39461 39462 } 39463 39464 break; 39465 39466 } 39467 39468}, 39469 39470 39471/** 39472* @method _onParentMenuRender 39473* @description "render" event handler for a submenu. Renders a 39474* submenu in response to the firing of its parent's "render" event. 39475* @private 39476* @param {String} p_sType String representing the name of the event that 39477* was fired. 39478* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39479* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 39480* subscribed to the event. 39481*/ 39482_onParentMenuRender: function (p_sType, p_aArgs, p_oSubmenu) { 39483 39484 var oParentMenu = p_oSubmenu.parent.parent, 39485 oParentCfg = oParentMenu.cfg, 39486 39487 oConfig = { 39488 39489 constraintoviewport: oParentCfg.getProperty(_CONSTRAIN_TO_VIEWPORT), 39490 39491 xy: [0,0], 39492 39493 clicktohide: oParentCfg.getProperty(_CLICK_TO_HIDE), 39494 39495 effect: oParentCfg.getProperty(_EFFECT), 39496 39497 showdelay: oParentCfg.getProperty(_SHOW_DELAY), 39498 39499 hidedelay: oParentCfg.getProperty(_HIDE_DELAY), 39500 39501 submenuhidedelay: oParentCfg.getProperty(_SUBMENU_HIDE_DELAY), 39502 39503 classname: oParentCfg.getProperty(_CLASSNAME), 39504 39505 scrollincrement: oParentCfg.getProperty(_SCROLL_INCREMENT), 39506 39507 maxheight: oParentCfg.getProperty(_MAX_HEIGHT), 39508 39509 minscrollheight: oParentCfg.getProperty(_MIN_SCROLL_HEIGHT), 39510 39511 iframe: oParentCfg.getProperty(_IFRAME), 39512 39513 shadow: oParentCfg.getProperty(_SHADOW), 39514 39515 preventcontextoverlap: oParentCfg.getProperty(_PREVENT_CONTEXT_OVERLAP), 39516 39517 monitorresize: oParentCfg.getProperty(_MONITOR_RESIZE), 39518 39519 keepopen: oParentCfg.getProperty(_KEEP_OPEN) 39520 39521 }, 39522 39523 oLI; 39524 39525 39526 39527 if (!(oParentMenu instanceof YAHOO.widget.MenuBar)) { 39528 39529 oConfig[_SUBMENU_ALIGNMENT] = oParentCfg.getProperty(_SUBMENU_ALIGNMENT); 39530 39531 } 39532 39533 39534 p_oSubmenu.cfg.applyConfig(oConfig); 39535 39536 39537 if (!this.lazyLoad) { 39538 39539 oLI = this.parent.element; 39540 39541 if (this.element.parentNode == oLI) { 39542 39543 this.render(); 39544 39545 } 39546 else { 39547 39548 this.render(oLI); 39549 39550 } 39551 39552 } 39553 39554}, 39555 39556 39557/** 39558* @method _onMenuItemDestroy 39559* @description "destroy" event handler for the menu's items. 39560* @private 39561* @param {String} p_sType String representing the name of the event 39562* that was fired. 39563* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39564* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 39565* that fired the event. 39566*/ 39567_onMenuItemDestroy: function (p_sType, p_aArgs, p_oItem) { 39568 39569 this._removeItemFromGroupByValue(p_oItem.groupIndex, p_oItem); 39570 39571}, 39572 39573 39574/** 39575* @method _onMenuItemConfigChange 39576* @description "configchange" event handler for the menu's items. 39577* @private 39578* @param {String} p_sType String representing the name of the event that 39579* was fired. 39580* @param {Array} p_aArgs Array of arguments sent when the event was fired.
39581* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 39582* that fired the event. 39583*/ 39584_onMenuItemConfigChange: function (p_sType, p_aArgs, p_oItem) { 39585 39586 var sPropertyName = p_aArgs[0][0], 39587 oPropertyValue = p_aArgs[0][1], 39588 oSubmenu; 39589 39590 39591 switch(sPropertyName) { 39592 39593 case _SELECTED: 39594 39595 if (oPropertyValue === true) { 39596 39597 this.activeItem = p_oItem; 39598 39599 } 39600 39601 break; 39602 39603 case _SUBMENU: 39604 39605 oSubmenu = p_aArgs[0][1]; 39606 39607 if (oSubmenu) { 39608 39609 this._configureSubmenu(p_oItem); 39610 39611 } 39612 39613 break; 39614 39615 } 39616 39617}, 39618 39619 39620 39621// Public event handlers for configuration properties 39622 39623 39624/** 39625* @method configVisible 39626* @description Event handler for when the "visible" configuration property 39627* the menu changes. 39628* @param {String} p_sType String representing the name of the event that 39629* was fired. 39630* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39631* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 39632* fired the event. 39633*/ 39634configVisible: function (p_sType, p_aArgs, p_oMenu) { 39635 39636 var bVisible, 39637 sDisplay; 39638 39639 if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 39640 39641 Menu.superclass.configVisible.call(this, p_sType, p_aArgs, p_oMenu); 39642 39643 } 39644 else { 39645 39646 bVisible = p_aArgs[0]; 39647 sDisplay = Dom.getStyle(this.element, _DISPLAY); 39648 39649 Dom.setStyle(this.element, _VISIBILITY, _VISIBLE); 39650 39651 if (bVisible) { 39652 39653 if (sDisplay != _BLOCK) { 39654 this.beforeShowEvent.fire(); 39655 Dom.setStyle(this.element, _DISPLAY, _BLOCK); 39656 this.showEvent.fire(); 39657 } 39658 39659 } 39660 else { 39661 39662 if (sDisplay == _BLOCK) { 39663 this.beforeHideEvent.fire(); 39664 Dom.setStyle(this.element, _DISPLAY, _NONE); 39665 this.hideEvent.fire(); 39666 } 39667 39668 } 39669 39670 } 39671 39672}, 39673 39674 39675/** 39676* @method configPosition 39677* @description Event handler for when the "position" configuration property 39678* of the menu changes. 39679* @param {String} p_sType String representing the name of the event that 39680* was fired. 39681* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39682* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 39683* fired the event. 39684*/ 39685configPosition: function (p_sType, p_aArgs, p_oMenu) { 39686 39687 var oElement = this.element, 39688 sCSSPosition = p_aArgs[0] == _STATIC ? _STATIC : _ABSOLUTE, 39689 oCfg = this.cfg, 39690 nZIndex; 39691 39692 39693 Dom.setStyle(oElement, _POSITION, sCSSPosition); 39694 39695 39696 if (sCSSPosition == _STATIC) { 39697 39698 // Statically positioned menus are visible by default 39699 39700 Dom.setStyle(oElement, _DISPLAY, _BLOCK); 39701 39702 oCfg.setProperty(_VISIBLE, true); 39703 39704 } 39705 else { 39706 39707 /* 39708 Even though the "visible" property is queued to 39709 "false" by default, we need to set the "visibility" property to 39710 "hidden" since Overlay's "configVisible" implementation checks the 39711 element's "visibility" style property before deciding whether 39712 or not to show an Overlay instance. 39713 */ 39714 39715 Dom.setStyle(oElement, _VISIBILITY, _HIDDEN); 39716 39717 } 39718 39719 39720 if (sCSSPosition == _ABSOLUTE) { 39721 nZIndex = oCfg.getProperty(_ZINDEX); 39722 39723 if (!nZIndex || nZIndex === 0) { 39724 oCfg.setProperty(_ZINDEX, 1); 39725 } 39726 39727 } 39728 39729}, 39730 39731 39732/** 39733* @method configIframe 39734* @description Event handler for when the "iframe" configuration property of 39735* the menu changes. 39736* @param {String} p_sType String representing the name of the event that 39737* was fired. 39738* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39739* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 39740* fired the event. 39741*/ 39742configIframe: function (p_sType, p_aArgs, p_oMenu) { 39743 39744 if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 39745 39746 Menu.superclass.configIframe.call(this, p_sType, p_aArgs, p_oMenu); 39747 39748 } 39749 39750}, 39751 39752 39753/** 39754* @method configHideDelay 39755* @description Event handler for when the "hidedelay" configuration property 39756* of the menu changes. 39757* @param {String}
39757 p_sType String representing the name of the event that 39758* was fired. 39759* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39760* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 39761* fired the event. 39762*/ 39763configHideDelay: function (p_sType, p_aArgs, p_oMenu) { 39764 39765 var nHideDelay = p_aArgs[0]; 39766 39767 this._useHideDelay = (nHideDelay > 0); 39768 39769}, 39770 39771 39772/** 39773* @method configContainer 39774* @description Event handler for when the "container" configuration property 39775* of the menu changes. 39776* @param {String} p_sType String representing the name of the event that 39777* was fired. 39778* @param {Array} p_aArgs Array of arguments sent when the event was fired. 39779* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 39780* fired the event. 39781*/ 39782configContainer: function (p_sType, p_aArgs, p_oMenu) { 39783 39784 var oElement = p_aArgs[0]; 39785 39786 if (Lang.isString(oElement)) { 39787 39788 this.cfg.setProperty(_CONTAINER, Dom.get(oElement), true); 39789 39790 } 39791 39792}, 39793 39794 39795/** 39796* @method _clearSetWidthFlag 39797* @description Change event listener for the "width" configuration property. This listener is 39798* added when a Menu's "width" configuration property is set by the "_setScrollHeight" method, and 39799* is used to set the "_widthSetForScroll" property to "false" if the "width" configuration property 39800* is changed after it was set by the "_setScrollHeight" method. If the "_widthSetForScroll" 39801* property is set to "false", and the "_setScrollHeight" method is in the process of tearing down 39802* scrolling functionality, it will maintain the Menu's new width rather than reseting it. 39803* @private 39804*/ 39805_clearSetWidthFlag: function () { 39806 39807 this._widthSetForScroll = false; 39808 39809 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); 39810 39811}, 39812 39813/** 39814 * @method _subscribeScrollHandlers 39815 * @param {HTMLElement} oHeader The scroll header element 39816 * @param {HTMLElement} oFooter The scroll footer element 39817 */ 39818_subscribeScrollHandlers : function(oHeader, oFooter) { 39819 var fnMouseOver = this._onScrollTargetMouseOver; 39820 var fnMouseOut = this._onScrollTargetMouseOut; 39821 39822 Event.on(oHeader, _MOUSEOVER, fnMouseOver, this, true); 39823 Event.on(oHeader, _MOUSEOUT, fnMouseOut, this, true); 39824 Event.on(oFooter, _MOUSEOVER, fnMouseOver, this, true); 39825 Event.on(oFooter, _MOUSEOUT, fnMouseOut, this, true); 39826}, 39827 39828/** 39829 * @method _unsubscribeScrollHandlers 39830 * @param {HTMLElement} oHeader The scroll header element 39831 * @param {HTMLElement} oFooter The scroll footer element 39832 */ 39833_unsubscribeScrollHandlers : function(oHeader, oFooter) { 39834 var fnMouseOver = this._onScrollTargetMouseOver; 39835 var fnMouseOut = this._onScrollTargetMouseOut; 39836 39837 Event.removeListener(oHeader, _MOUSEOVER, fnMouseOver); 39838 Event.removeListener(oHeader, _MOUSEOUT, fnMouseOut); 39839 Event.removeListener(oFooter, _MOUSEOVER, fnMouseOver); 39840 Event.removeListener(oFooter, _MOUSEOUT, fnMouseOut); 39841}, 39842 39843/** 39844* @method _setScrollHeight 39845* @description 39846* @param {String} p_nScrollHeight Number representing the scrolling height of the Menu. 39847* @private 39848*/ 39849_setScrollHeight: function (p_nScrollHeight) { 39850 39851 var nScrollHeight = p_nScrollHeight, 39852 bRefireIFrameAndShadow = false, 39853 bSetWidth = false, 39854 oElement, 39855 oBody, 39856 oHeader, 39857 oFooter, 39858 nMinScrollHeight, 39859 nHeight, 39860 nOffsetWidth, 39861 sWidth; 39862 39863 if (this.getItems().length > 0) { 39864 39865 oElement = this.element; 39866 oBody = this.body; 39867 oHeader = this.header; 39868 oFooter = this.footer; 39869 nMinScrollHeight = this.cfg.getProperty(_MIN_SCROLL_HEIGHT); 39870 39871 if (nScrollHeight > 0 && nScrollHeight < nMinScrollHeight) { 39872 nScrollHeight = nMinScrollHeight; 39873 } 39874 39875 Dom.setStyle(oBody, _HEIGHT, _EMPTY_STRING); 39876 Dom.removeClass(oBody, _YUI_MENU_BODY_SCROLLED); 39877 oBody.scrollTop = 0; 39878 39879 // Need to set a width for the Menu to fix the following problems in 39880 // Firefox 2 and IE: 39881 39882 // #1) Scrolled Menus will render at 1px wide in Firefox 2 39883 39884 // #2) There is a bug in gecko-based browsers where an element whose 39885 // "position" property is set to "absolute" and "overflow" property is 39886 // set to "hidden" will not render at the correct width when its 39887 // offsetParent's "position" property is also set to "absolute." It is 39888 // possible to work around this bug by specifying a value for the width 39889 // property in addition to overflow. 39890 39891 // #3) In IE it is necessary to give the Menu a width before the 39892 // scrollbars are rendered to prevent the Menu from rendering with a 39893 // width that is 100% of the browser viewport. 39894
39895 bSetWidth = ((UA.gecko && UA.gecko < 1.9) || UA.ie); 39896 39897 if (nScrollHeight > 0 && bSetWidth && !this.cfg.getProperty(_WIDTH)) { 39898 39899 nOffsetWidth = oElement.offsetWidth; 39900 39901 /* 39902 Measuring the difference of the offsetWidth before and after 39903 setting the "width" style attribute allows us to compute the 39904 about of padding and borders applied to the element, which in 39905 turn allows us to set the "width" property correctly. 39906 */ 39907 39908 oElement.style.width = nOffsetWidth + _PX; 39909 39910 sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX; 39911 39912 39913 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); 39914 39915 YAHOO.log("Setting the \"width\" configuration property to " + sWidth + " for srolling.", 39916 "info", this.toString()); 39917 39918 this.cfg.setProperty(_WIDTH, sWidth); 39919 39920 39921 /* 39922 Set a flag (_widthSetForScroll) to maintain some history regarding how the 39923 "width" configuration property was set. If the "width" configuration property 39924 is set by something other than the "_setScrollHeight" method, it will be 39925 necessary to maintain that new value and not clear the width if scrolling 39926 is turned off. 39927 */ 39928 39929 this._widthSetForScroll = true; 39930 39931 this.cfg.subscribeToConfigEvent(_WIDTH, this._clearSetWidthFlag); 39932 39933 } 39934 39935 39936 if (nScrollHeight > 0 && (!oHeader && !oFooter)) { 39937 39938 YAHOO.log("Creating header and footer for scrolling.", "info", this.toString()); 39939 39940 this.setHeader(_NON_BREAKING_SPACE); 39941 this.setFooter(_NON_BREAKING_SPACE); 39942 39943 oHeader = this.header; 39944 oFooter = this.footer; 39945 39946 Dom.addClass(oHeader, _TOP_SCROLLBAR); 39947 Dom.addClass(oFooter, _BOTTOM_SCROLLBAR); 39948 39949 oElement.insertBefore(oHeader, oBody); 39950 oElement.appendChild(oFooter); 39951 39952 } 39953 39954 nHeight = nScrollHeight; 39955 39956 if (oHeader && oFooter) { 39957 nHeight = (nHeight - (oHeader.offsetHeight + oFooter.offsetHeight)); 39958 } 39959 39960 39961 if ((nHeight > 0) && (oBody.offsetHeight > nScrollHeight)) { 39962 39963 YAHOO.log("Setting up styles and event handlers for scrolling.", 39964 "info", this.toString()); 39965 39966 Dom.addClass(oBody, _YUI_MENU_BODY_SCROLLED); 39967 Dom.setStyle(oBody, _HEIGHT, (nHeight + _PX)); 39968 39969 if (!this._hasScrollEventHandlers) { 39970 this._subscribeScrollHandlers(oHeader, oFooter); 39971 this._hasScrollEventHandlers = true; 39972 } 39973 39974 this._disableScrollHeader(); 39975 this._enableScrollFooter(); 39976 39977 bRefireIFrameAndShadow = true; 39978 39979 } 39980 else if (oHeader && oFooter) { 39981 39982 YAHOO.log("Removing styles and event handlers for scrolling.", "info", this.toString()); 39983 39984 39985 /* 39986 Only clear the the "width" configuration property if it was set the 39987 "_setScrollHeight" method and wasn't changed by some other means after it was set. 39988 */ 39989 39990 if (this._widthSetForScroll) { 39991 39992 YAHOO.log("Clearing width used for scrolling.", "info", this.toString()); 39993 39994 this._widthSetForScroll = false; 39995 39996 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); 39997 39998 this.cfg.setProperty(_WIDTH, _EMPTY_STRING); 39999 40000 } 40001 40002 40003 this._enableScrollHeader(); 40004 this._enableScrollFooter(); 40005 40006 if (this._hasScrollEventHandlers) { 40007 this._unsubscribeScrollHandlers(oHeader, oFooter); 40008 this._hasScrollEventHandlers = false; 40009 } 40010 40011 oElement.removeChild(oHeader); 40012 oElement.removeChild(oFooter); 40013 40014 this.header = null; 40015 this.footer = null; 40016 40017 bRefireIFrameAndShadow = true; 40018 40019 } 40020 40021 40022 if (bRefireIFrameAndShadow) { 40023 40024 this.cfg.refireEvent(_IFRAME); 40025 this.cfg.refireEvent(_SHADOW); 40026 40027 } 40028 40029 } 40030 40031}, 40032 40033 40034/** 40035* @method _setMaxHeight 40036* @description "renderEvent" handler used to defer the setting of the 40037* "maxheight" configuration property until the menu is rendered in lazy 40038* load scenarios. 40039* @param {String} p_sType The name of the event that was fired. 40040* @param {Array} p_aArgs Collection of arguments sent when the event 40041* was fired. 40042* @param {Number} p_nMaxHeight Number representing the value to set for the 40043* "maxheight" configuration property. 40044* @private 40045*/ 40046_setMaxHeight: function (p_sType, p_aArgs, p_nMaxHeight) { 40047 40048 this._setScrollHeight(p_nMaxHeight); 40049 this.renderEvent.unsubscribe(this._setMaxHeight); 40050 40051}, 40052 40053 40054/** 40055* @method configMaxHeight
40056* @description Event handler for when the "maxheight" configuration property of 40057* a Menu changes. 40058* @param {String} p_sType The name of the event that was fired. 40059* @param {Array} p_aArgs Collection of arguments sent when the event 40060* was fired. 40061* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired 40062* the event. 40063*/ 40064configMaxHeight: function (p_sType, p_aArgs, p_oMenu) { 40065 40066 var nMaxHeight = p_aArgs[0]; 40067 40068 if (this.lazyLoad && !this.body && nMaxHeight > 0) { 40069 40070 this.renderEvent.subscribe(this._setMaxHeight, nMaxHeight, this); 40071 40072 } 40073 else { 40074 40075 this._setScrollHeight(nMaxHeight); 40076 40077 } 40078 40079}, 40080 40081 40082/** 40083* @method configClassName 40084* @description Event handler for when the "classname" configuration property of 40085* a menu changes. 40086* @param {String} p_sType The name of the event that was fired. 40087* @param {Array} p_aArgs Collection of arguments sent when the event was fired. 40088* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. 40089*/ 40090configClassName: function (p_sType, p_aArgs, p_oMenu) { 40091 40092 var sClassName = p_aArgs[0]; 40093 40094 if (this._sClassName) { 40095 40096 Dom.removeClass(this.element, this._sClassName); 40097 40098 } 40099 40100 Dom.addClass(this.element, sClassName); 40101 this._sClassName = sClassName; 40102 40103}, 40104 40105 40106/** 40107* @method _onItemAdded 40108* @description "itemadded" event handler for a Menu instance. 40109* @private 40110* @param {String} p_sType The name of the event that was fired. 40111* @param {Array} p_aArgs Collection of arguments sent when the event 40112* was fired. 40113*/ 40114_onItemAdded: function (p_sType, p_aArgs) { 40115 40116 var oItem = p_aArgs[0]; 40117 40118 if (oItem) { 40119 40120 oItem.cfg.setProperty(_DISABLED, true); 40121 40122 } 40123 40124}, 40125 40126 40127/** 40128* @method configDisabled 40129* @description Event handler for when the "disabled" configuration property of 40130* a menu changes. 40131* @param {String} p_sType The name of the event that was fired. 40132* @param {Array} p_aArgs Collection of arguments sent when the event was fired. 40133* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. 40134*/ 40135configDisabled: function (p_sType, p_aArgs, p_oMenu) { 40136 40137 var bDisabled = p_aArgs[0], 40138 aItems = this.getItems(), 40139 nItems, 40140 i; 40141 40142 if (Lang.isArray(aItems)) { 40143 40144 nItems = aItems.length; 40145 40146 if (nItems > 0) { 40147 40148 i = nItems - 1; 40149 40150 do { 40151 40152 aItems[i].cfg.setProperty(_DISABLED, bDisabled); 40153 40154 } 40155 while (i--); 40156 40157 } 40158 40159 40160 if (bDisabled) { 40161 40162 this.clearActiveItem(true); 40163 40164 Dom.addClass(this.element, _DISABLED); 40165 40166 this.itemAddedEvent.subscribe(this._onItemAdded); 40167 40168 } 40169 else { 40170 40171 Dom.removeClass(this.element, _DISABLED); 40172 40173 this.itemAddedEvent.unsubscribe(this._onItemAdded); 40174 40175 } 40176 40177 } 40178 40179}, 40180 40181/** 40182 * Resizes the shadow to match the container bounding element 40183 * 40184 * @method _sizeShadow 40185 * @protected 40186 */ 40187_sizeShadow : function () { 40188 40189 var oElement = this.element, 40190 oShadow = this._shadow; 40191 40192 if (oShadow && oElement) { 40193 // Clear the previous width 40194 if (oShadow.style.width && oShadow.style.height) { 40195 oShadow.style.width = _EMPTY_STRING; 40196 oShadow.style.height = _EMPTY_STRING; 40197 } 40198 40199 oShadow.style.width = (oElement.offsetWidth + 6) + _PX; 40200 oShadow.style.height = (oElement.offsetHeight + 1) + _PX; 40201 } 40202}, 40203 40204/** 40205 * Replaces the shadow element in the DOM with the current shadow element (this._shadow) 40206 * 40207 * @method _replaceShadow 40208 * @protected 40209 */ 40210_replaceShadow : function () { 40211 this.element.appendChild(this._shadow); 40212}, 40213 40214/** 40215 * Adds the classname marker for a visible shadow, to the shadow element 40216 * 40217 * @method _addShadowVisibleClass 40218 * @protected 40219 */ 40220_addShadowVisibleClass : function () { 40221 Dom.addClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE); 40222}, 40223 40224/** 40225 * Removes the classname marker for a visible shadow, from the shadow element 40226 * 40227 * @method _removeShadowVisibleClass 40228 * @protected 40229 */ 40230_removeShadowVisibleClass : function () { 40231 Dom.removeClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE); 40232}, 40233 40234/** 40235 * Removes the shadow element from the DOM, and unsubscribes all the listeners used to keep it in sync. Used
40236 * to handle setting the shadow to false. 40237 * 40238 * @method _removeShadow 40239 * @protected 40240 */ 40241_removeShadow : function() { 40242 40243 var p = (this._shadow && this._shadow.parentNode); 40244 40245 if (p) { 40246 p.removeChild(this._shadow); 40247 } 40248 40249 this.beforeShowEvent.unsubscribe(this._addShadowVisibleClass); 40250 this.beforeHideEvent.unsubscribe(this._removeShadowVisibleClass); 40251 40252 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._sizeShadow); 40253 this.cfg.unsubscribeFromConfigEvent(_HEIGHT, this._sizeShadow); 40254 this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._sizeShadow); 40255 this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._replaceShadow); 40256 40257 this.changeContentEvent.unsubscribe(this._sizeShadow); 40258 40259 Module.textResizeEvent.unsubscribe(this._sizeShadow); 40260}, 40261 40262/** 40263 * Used to create the shadow element, add it to the DOM, and subscribe listeners to keep it in sync. 40264 * 40265 * @method _createShadow 40266 * @protected 40267 */ 40268_createShadow : function () { 40269 40270 var oShadow = this._shadow, 40271 oElement; 40272 40273 if (!oShadow) { 40274 oElement = this.element; 40275 40276 if (!m_oShadowTemplate) { 40277 m_oShadowTemplate = document.createElement(_DIV_LOWERCASE); 40278 m_oShadowTemplate.className = _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE; 40279 } 40280 40281 oShadow = m_oShadowTemplate.cloneNode(false); 40282 40283 oElement.appendChild(oShadow); 40284 40285 this._shadow = oShadow; 40286 40287 this.beforeShowEvent.subscribe(this._addShadowVisibleClass); 40288 this.beforeHideEvent.subscribe(this._removeShadowVisibleClass); 40289 40290 if (UA.ie) { 40291 /* 40292 Need to call sizeShadow & syncIframe via setTimeout for 40293 IE 7 Quirks Mode and IE 6 Standards Mode and Quirks Mode 40294 or the shadow and iframe shim will not be sized and 40295 positioned properly. 40296 */ 40297 Lang.later(0, this, function () { 40298 this._sizeShadow(); 40299 this.syncIframe(); 40300 }); 40301 40302 this.cfg.subscribeToConfigEvent(_WIDTH, this._sizeShadow); 40303 this.cfg.subscribeToConfigEvent(_HEIGHT, this._sizeShadow); 40304 this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._sizeShadow); 40305 this.changeContentEvent.subscribe(this._sizeShadow); 40306 40307 Module.textResizeEvent.subscribe(this._sizeShadow, this, true); 40308 40309 this.destroyEvent.subscribe(function () { 40310 Module.textResizeEvent.unsubscribe(this._sizeShadow, this); 40311 }); 40312 } 40313 40314 this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._replaceShadow); 40315 } 40316}, 40317 40318/** 40319 * The beforeShow event handler used to set up the shadow lazily when the menu is made visible. 40320 * @method _shadowBeforeShow 40321 * @protected 40322 */ 40323_shadowBeforeShow : function () { 40324 if (this._shadow) { 40325 40326 // If called because the "shadow" event was refired - just append again and resize 40327 this._replaceShadow(); 40328 40329 if (UA.ie) { 40330 this._sizeShadow(); 40331 } 40332 } else { 40333 this._createShadow(); 40334 } 40335 40336 this.beforeShowEvent.unsubscribe(this._shadowBeforeShow); 40337}, 40338 40339/** 40340* @method configShadow 40341* @description Event handler for when the "shadow" configuration property of 40342* a menu changes. 40343* @param {String} p_sType The name of the event that was fired. 40344* @param {Array} p_aArgs Collection of arguments sent when the event was fired. 40345* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. 40346*/ 40347configShadow: function (p_sType, p_aArgs, p_oMenu) { 40348 40349 var bShadow = p_aArgs[0]; 40350 40351 if (bShadow && this.cfg.getProperty(_POSITION) == _DYNAMIC) { 40352 if (this.cfg.getProperty(_VISIBLE)) { 40353 if (this._shadow) { 40354 // If the "shadow" event was refired - just append again and resize 40355 this._replaceShadow(); 40356 40357 if (UA.ie) { 40358 this._sizeShadow(); 40359 } 40360 } else { 40361 this._createShadow(); 40362 } 40363 } else { 40364 this.beforeShowEvent.subscribe(this._shadowBeforeShow); 40365 } 40366 } else if (!bShadow) { 40367 this.beforeShowEvent.unsubscribe(this._shadowBeforeShow); 40368 this._removeShadow(); 40369 } 40370}, 40371 40372// Public methods 40373 40374/** 40375* @method initEvents 40376* @description Initializes the custom events for the menu. 40377*/ 40378initEvents: function () { 40379 40380 Menu.superclass.initEvents.call(this); 40381 40382 // Create custom events 40383 40384 var i = EVENT_TYPES.length - 1, 40385 aEventData, 40386 oCustomEvent; 40387 40388 40389 do { 40390 40391 aEventData = EVENT_TYPES[i]; 40392 40393 oCustomEvent = this.createEvent(aEventData[1]);
40394 oCustomEvent.signature = CustomEvent.LIST; 40395 40396 this[aEventData[0]] = oCustomEvent; 40397 40398 } 40399 while (i--); 40400 40401}, 40402 40403 40404/** 40405* @method positionOffScreen 40406* @description Positions the menu outside of the boundaries of the browser's 40407* viewport. Called automatically when a menu is hidden to ensure that 40408* it doesn't force the browser to render uncessary scrollbars. 40409*/ 40410positionOffScreen: function () { 40411 40412 var oIFrame = this.iframe, 40413 oElement = this.element, 40414 sPos = this.OFF_SCREEN_POSITION; 40415 40416 oElement.style.top = _EMPTY_STRING; 40417 oElement.style.left = _EMPTY_STRING; 40418 40419 if (oIFrame) { 40420 40421 oIFrame.style.top = sPos; 40422 oIFrame.style.left = sPos; 40423 40424 } 40425 40426}, 40427 40428 40429/** 40430* @method getRoot 40431* @description Finds the menu's root menu. 40432*/ 40433getRoot: function () { 40434 40435 var oItem = this.parent, 40436 oParentMenu, 40437 returnVal; 40438 40439 if (oItem) { 40440 40441 oParentMenu = oItem.parent; 40442 40443 returnVal = oParentMenu ? oParentMenu.getRoot() : this; 40444 40445 } 40446 else { 40447 40448 returnVal = this; 40449 40450 } 40451 40452 return returnVal; 40453 40454}, 40455 40456 40457/** 40458* @method toString 40459* @description Returns a string representing the menu. 40460* @return {String} 40461*/ 40462toString: function () { 40463 40464 var sReturnVal = _MENU, 40465 sId = this.id; 40466 40467 if (sId) { 40468 40469 sReturnVal += (_SPACE + sId); 40470 40471 } 40472 40473 return sReturnVal; 40474 40475}, 40476 40477 40478/** 40479* @method setItemGroupTitle 40480* @description Sets the title of a group of menu items. 40481* @param {HTML} p_sGroupTitle String or markup specifying the title of the group. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 40482* @param {Number} p_nGroupIndex Optional. Number specifying the group to which 40483* the title belongs. 40484*/ 40485setItemGroupTitle: function (p_sGroupTitle, p_nGroupIndex) { 40486 40487 var nGroupIndex, 40488 oTitle, 40489 i, 40490 nFirstIndex; 40491 40492 if (Lang.isString(p_sGroupTitle) && p_sGroupTitle.length > 0) { 40493 40494 nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0; 40495 oTitle = this._aGroupTitleElements[nGroupIndex]; 40496 40497 40498 if (oTitle) { 40499 40500 oTitle.innerHTML = p_sGroupTitle; 40501 40502 } 40503 else { 40504 40505 oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME); 40506 40507 oTitle.innerHTML = p_sGroupTitle; 40508 40509 this._aGroupTitleElements[nGroupIndex] = oTitle; 40510 40511 } 40512 40513 40514 i = this._aGroupTitleElements.length - 1; 40515 40516 do { 40517 40518 if (this._aGroupTitleElements[i]) { 40519 40520 Dom.removeClass(this._aGroupTitleElements[i], _FIRST_OF_TYPE); 40521 40522 nFirstIndex = i; 40523 40524 } 40525 40526 } 40527 while (i--); 40528 40529 40530 if (nFirstIndex !== null) { 40531 40532 Dom.addClass(this._aGroupTitleElements[nFirstIndex], 40533 _FIRST_OF_TYPE); 40534 40535 } 40536 40537 this.changeContentEvent.fire(); 40538 40539 } 40540 40541}, 40542 40543 40544 40545/** 40546* @method addItem 40547* @description Appends an item to the menu. 40548* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 40549* instance to be added to the menu. 40550* @param {HTML} p_oItem String or markup specifying content of the item to be added 40551* to the menu. The item text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 40552* @param {Object} p_oItem Object literal containing a set of menu item 40553* configuration properties. 40554* @param {Number} p_nGroupIndex Optional. Number indicating the group to 40555* which the item belongs. 40556* @return {YAHOO.widget.MenuItem} 40557*/ 40558addItem: function (p_oItem, p_nGroupIndex) { 40559 40560 return this._addItemToGroup(p_nGroupIndex, p_oItem); 40561 40562}, 40563 40564 40565/** 40566* @method addItems 40567* @description Adds an array of items to the menu. 40568* @param {Array} p_aItems Array of items to be added to the menu. The array 40569* can contain strings specifying the markup for the content of each item to be created, object 40570* literals specifying each of the menu item configuration properties, 40571* or MenuItem instances. The item content if provided as a string is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 40572* @param {Number} p_nGroupIndex Optional. Number specifying the group to 40573* which the items belongs. 40574* @return {Array} 40575*/ 40576addItems: function (p_aItems, p_nGroupIndex) { 40577 40578 var nItems, 40579 aItems, 40580 oItem, 40581 i, 40582 returnVal; 40583 40584 40585 if (Lang.isArray(p_aItems)) { 40586 40587 nItems = p_aItems.length; 40588 aItems = []; 40589 40590 for(i=0; i<nItems; i++) { 40591 40592 oItem = p_aItems[i]; 40593 40594 if (oItem) { 40595 40596 if (Lang.isArray(oItem)) { 40597 40598 aItems[aItems.length] = this.addItems(oItem, i); 40599 40600 } 40601 else { 40602 40603 aItems[aItems.length] = this._addItemToGroup(p_nGroupIndex, oItem); 40604 40605 } 40606 40607 } 40608 40609 } 40610 40611 40612 if (aItems.length) { 40613 40614 returnVal = aItems; 40615 40616 } 40617 40618 } 40619 40620 return returnVal; 40621 40622}, 40623 40624 40625/** 40626* @method insertItem 40627* @description Inserts an item into the menu at the specified index.
40628* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 40629* instance to be added to the menu. 40630* @param {String} p_oItem String specifying the text of the item to be added 40631* to the menu. 40632* @param {Object} p_oItem Object literal containing a set of menu item 40633* configuration properties. 40634* @param {Number} p_nItemIndex Number indicating the ordinal position at which 40635* the item should be added. 40636* @param {Number} p_nGroupIndex Optional. Number indicating the group to which 40637* the item belongs. 40638* @return {YAHOO.widget.MenuItem} 40639*/ 40640insertItem: function (p_oItem, p_nItemIndex, p_nGroupIndex) { 40641 40642 return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex); 40643 40644}, 40645 40646 40647/** 40648* @method removeItem 40649* @description Removes the specified item from the menu. 40650* @param {YAHOO.widget.MenuItem} p_oObject Object reference for the MenuItem 40651* instance to be removed from the menu. 40652* @param {Number} p_oObject Number specifying the index of the item 40653* to be removed. 40654* @param {Number} p_nGroupIndex Optional. Number specifying the group to 40655* which the item belongs. 40656* @return {YAHOO.widget.MenuItem} 40657*/ 40658removeItem: function (p_oObject, p_nGroupIndex) { 40659 40660 var oItem, 40661 returnVal; 40662 40663 if (!Lang.isUndefined(p_oObject)) { 40664 40665 if (p_oObject instanceof YAHOO.widget.MenuItem) { 40666 40667 oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject); 40668 40669 } 40670 else if (Lang.isNumber(p_oObject)) { 40671 40672 oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject); 40673 40674 } 40675 40676 if (oItem) { 40677 40678 oItem.destroy(); 40679 40680 YAHOO.log("Item removed." + 40681 " Text: " + oItem.cfg.getProperty("text") + ", " + 40682 " Index: " + oItem.index + ", " + 40683 " Group Index: " + oItem.groupIndex, "info", this.toString()); 40684 40685 returnVal = oItem; 40686 40687 } 40688 40689 } 40690 40691 return returnVal; 40692 40693}, 40694 40695 40696/** 40697* @method getItems 40698* @description Returns an array of all of the items in the menu. 40699* @return {Array} 40700*/ 40701getItems: function () { 40702 40703 var aGroups = this._aItemGroups, 40704 nGroups, 40705 returnVal, 40706 aItems = []; 40707 40708 40709 if (Lang.isArray(aGroups)) { 40710 40711 nGroups = aGroups.length; 40712 40713 returnVal = ((nGroups == 1) ? aGroups[0] : (Array.prototype.concat.apply(aItems, aGroups))); 40714 40715 } 40716 40717 return returnVal; 40718 40719}, 40720 40721 40722/** 40723* @method getItemGroups 40724* @description Multi-dimensional Array representing the menu items as they 40725* are grouped in the menu. 40726* @return {Array} 40727*/ 40728getItemGroups: function () { 40729 40730 return this._aItemGroups; 40731 40732}, 40733 40734 40735/** 40736* @method getItem 40737* @description Returns the item at the specified index. 40738* @param {Number} p_nItemIndex Number indicating the ordinal position of the 40739* item to be retrieved. 40740* @param {Number} p_nGroupIndex Optional. Number indicating the group to which 40741* the item belongs. 40742* @return {YAHOO.widget.MenuItem} 40743*/ 40744getItem: function (p_nItemIndex, p_nGroupIndex) { 40745 40746 var aGroup, 40747 returnVal; 40748 40749 if (Lang.isNumber(p_nItemIndex)) { 40750 40751 aGroup = this._getItemGroup(p_nGroupIndex); 40752 40753 if (aGroup) { 40754 40755 returnVal = aGroup[p_nItemIndex]; 40756 40757 } 40758 40759 } 40760 40761 return returnVal; 40762 40763}, 40764 40765 40766/** 40767* @method getSubmenus 40768* @description Returns an array of all of the submenus that are immediate 40769* children of the menu. 40770* @return {Array} 40771*/ 40772getSubmenus: function () { 40773 40774 var aItems = this.getItems(), 40775 nItems = aItems.length, 40776 aSubmenus, 40777 oSubmenu, 40778 oItem, 40779 i; 40780 40781 40782 if (nItems > 0) { 40783 40784 aSubmenus = []; 40785 40786 for(i=0; i<nItems; i++) { 40787 40788 oItem = aItems[i]; 40789 40790 if (oItem) { 40791 40792 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 40793 40794 if (oSubmenu) { 40795 40796 aSubmenus[aSubmenus.length] = oSubmenu; 40797 40798 } 40799 40800 } 40801 40802 } 40803 40804 } 40805 40806 return aSubmenus; 40807 40808}, 40809 40810 40811/** 40812* @method clearContent 40813* @description Removes all of the content from the menu, including the menu 40814* items, group titles, header and footer. 40815*/ 40816clearContent: function () { 40817 40818 var aItems = this.getItems(), 40819 nItems = aItems.length, 40820 oElement = this.element, 40821 oBody = this.body, 40822 oHeader = this.header, 40823 oFooter = this.footer, 40824 oItem, 40825 oSubmenu, 40826 i; 40827 40828 40829 if (nItems > 0) { 40830 40831 i = nItems - 1; 40832 40833 do { 40834 40835 oItem = aItems[i]; 40836 40837 if (oItem) { 40838 40839 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 40840 40841 if (oSubmenu) { 40842 40843 this.cfg.configChangedEvent.unsubscribe( 40844 this._onParentMenuConfigChange, oSubmenu); 40845 40846 this.renderEvent.unsubscribe(this._onParentMenuRender, 40847 oSubmenu); 40848 40849 } 40850 40851 this.removeItem(oItem, oItem.groupIndex); 40852 40853 } 40854 40855 } 40856 while (i--); 40857 40858 } 40859 40860 40861 if (oHeader) { 40862 40863 Event.purgeElement(oHeader); 40864 oElement.removeChild(oHeader); 40865 40866 } 40867 40868 40869 if (oFooter) { 40870 40871 Event.purgeElement(oFooter); 40872 oElement.removeChild(oFooter); 40873 } 40874 40875 40876 if (oBody) { 40877 40878 Event.purgeElement(oBody); 40879 40880 oBody.innerHTML = _EMPTY_STRING; 40881 40882 } 40883 40884 this.activeItem = null; 40885 40886 this._aItemGroups = []; 40887 this._aListElements = []; 40888 this._aGroupTitleElements = []; 40889 40890 this.cfg.setProperty(_WIDTH, null); 40891 40892}, 40893 40894 40895/** 40896* @method destroy 40897* @description Removes the menu's <code><div></code> element 40898* (and accompanying child nodes) from the document. 40899* @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 40900* NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 40901* 40902*/ 40903destroy: function (shallowPurge) { 40904 40905 // Remove all items 40906 40907 this.clearContent(); 40908 40909 this._aItemGroups = null; 40910 this._aListElements = null; 40911 this._aGroupTitleElements = null; 40912 40913 40914 // Continue with the superclass implementation of this method 40915 40916 Menu.superclass.destroy.call(this, shallowPurge); 40917 40918 YAHOO.log("Destroyed.", "info", this.toString()); 40919 40920}, 40921 40922 40923/** 40924* @method setInitialFocus 40925* @description Sets focus to the menu's first enabled item. 40926*/ 40927setInitialFocus: function () { 40928 40929 var oItem = this._getFirstEnabledItem(); 40930 40931 if (oItem) { 40932 40933 oItem.focus(); 40934 40935 } 40936 40937}, 40938 40939 40940/** 40941* @method setInitialSelection 40942* @description Sets the "selected" configuration property of the menu's first 40943* enabled item to "true." 40944*/ 40945setInitialSelection: function () { 40946 40947 var oItem = this._getFirstEnabledItem(); 40948 40949 if (oItem) { 40950 40951 oItem.cfg.setProperty(_SELECTED, true); 40952 } 40953 40954}, 40955 40956 40957/** 40958* @method clearActiveItem 40959* @description Sets the "selected" configuration property of the menu's active
40960* item to "false" and hides the item's submenu. 40961* @param {Boolean} p_bBlur Boolean indicating if the menu's active item 40962* should be blurred. 40963*/ 40964clearActiveItem: function (p_bBlur) { 40965 40966 if (this.cfg.getProperty(_SHOW_DELAY) > 0) { 40967 40968 this._cancelShowDelay(); 40969 40970 } 40971 40972 40973 var oActiveItem = this.activeItem, 40974 oConfig, 40975 oSubmenu; 40976 40977 if (oActiveItem) { 40978 40979 oConfig = oActiveItem.cfg; 40980 40981 if (p_bBlur) { 40982 40983 oActiveItem.blur(); 40984 40985 this.getRoot()._hasFocus = true; 40986 40987 } 40988 40989 oConfig.setProperty(_SELECTED, false); 40990 40991 oSubmenu = oConfig.getProperty(_SUBMENU); 40992 40993 40994 if (oSubmenu) { 40995 40996 oSubmenu.hide(); 40997 40998 } 40999 41000 this.activeItem = null; 41001 41002 } 41003 41004}, 41005 41006 41007/** 41008* @method focus 41009* @description Causes the menu to receive focus and fires the "focus" event. 41010*/ 41011focus: function () { 41012 41013 if (!this.hasFocus()) { 41014 41015 this.setInitialFocus(); 41016 41017 } 41018 41019}, 41020 41021 41022/** 41023* @method blur 41024* @description Causes the menu to lose focus and fires the "blur" event. 41025*/ 41026blur: function () { 41027 41028 var oItem; 41029 41030 if (this.hasFocus()) { 41031 41032 oItem = MenuManager.getFocusedMenuItem(); 41033 41034 if (oItem) { 41035 41036 oItem.blur(); 41037 41038 } 41039 41040 } 41041 41042}, 41043 41044 41045/** 41046* @method hasFocus 41047* @description Returns a boolean indicating whether or not the menu has focus. 41048* @return {Boolean} 41049*/ 41050hasFocus: function () { 41051 41052 return (MenuManager.getFocusedMenu() == this.getRoot()); 41053 41054}, 41055 41056 41057_doItemSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) { 41058 41059 var oItem = p_aArgs[0], 41060 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 41061 41062 if (oSubmenu) { 41063 oSubmenu.subscribe.apply(oSubmenu, p_oObject); 41064 } 41065 41066}, 41067 41068 41069_doSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) { 41070 41071 var oSubmenu = this.cfg.getProperty(_SUBMENU); 41072 41073 if (oSubmenu) { 41074 oSubmenu.subscribe.apply(oSubmenu, p_oObject); 41075 } 41076 41077}, 41078 41079 41080/** 41081* Adds the specified CustomEvent subscriber to the menu and each of 41082* its submenus. 41083* @method subscribe 41084* @param p_type {string} the type, or name of the event 41085* @param p_fn {function} the function to exectute when the event fires 41086* @param p_obj {Object} An object to be passed along when the event 41087* fires 41088* @param p_override {boolean} If true, the obj passed in becomes the 41089* execution scope of the listener 41090*/ 41091subscribe: function () { 41092 41093 // Subscribe to the event for this Menu instance 41094 Menu.superclass.subscribe.apply(this, arguments); 41095 41096 // Subscribe to the "itemAdded" event so that all future submenus 41097 // also subscribe to this event 41098 Menu.superclass.subscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments); 41099 41100 41101 var aItems = this.getItems(), 41102 nItems, 41103 oItem, 41104 oSubmenu, 41105 i; 41106 41107 41108 if (aItems) { 41109 41110 nItems = aItems.length; 41111 41112 if (nItems > 0) { 41113 41114 i = nItems - 1; 41115 41116 do { 41117 41118 oItem = aItems[i]; 41119 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 41120 41121 if (oSubmenu) { 41122 oSubmenu.subscribe.apply(oSubmenu, arguments); 41123 } 41124 else { 41125 oItem.cfg.subscribeToConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments); 41126 } 41127 41128 } 41129 while (i--); 41130 41131 } 41132 41133 } 41134 41135}, 41136 41137 41138unsubscribe: function () { 41139 41140 // Remove the event for this Menu instance 41141 Menu.superclass.unsubscribe.apply(this, arguments); 41142 41143 // Remove the "itemAdded" event so that all future submenus don't have 41144 // the event handler 41145 Menu.superclass.unsubscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments); 41146 41147 41148 var aItems = this.getItems(), 41149 nItems, 41150 oItem, 41151 oSubmenu, 41152 i; 41153 41154 41155 if (aItems) { 41156 41157 nItems = aItems.length; 41158 41159 if (nItems > 0) { 41160 41161 i = nItems - 1; 41162 41163 do { 41164 41165 oItem = aItems[i]; 41166 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 41167 41168 if (oSubmenu) { 41169 oSubmenu.unsubscribe.apply(oSubmenu, arguments); 41170 } 41171 else { 41172 oItem.cfg.unsubscribeFromConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments); 41173 } 41174 41175 } 41176 while (i--); 41177 41178 } 41179 41180 } 41181 41182}, 41183 41184 41185/** 41186* @description Initializes the class's configurable properties which can be 41187* changed using the menu's Config object ("cfg"). 41188* @method initDefaultConfig 41189*/ 41190initDefaultConfig: function () { 41191 41192 Menu.superclass.initDefaultConfig.call(this); 41193 41194 var oConfig = this.cfg; 41195 41196 41197 // Module documentation overrides 41198 41199 /** 41200 * @config effect
41201 * @description Object or array of objects representing the ContainerEffect 41202 * classes that are active for animating the container. When set this 41203 * property is automatically applied to all submenus. 41204 * @type Object 41205 * @default null 41206 */ 41207 41208 // Overlay documentation overrides 41209 41210 41211 /** 41212 * @config x 41213 * @description Number representing the absolute x-coordinate position of 41214 * the Menu. This property is only applied when the "position" 41215 * configuration property is set to dynamic. 41216 * @type Number 41217 * @default null 41218 */ 41219 41220 41221 /** 41222 * @config y 41223 * @description Number representing the absolute y-coordinate position of 41224 * the Menu. This property is only applied when the "position" 41225 * configuration property is set to dynamic. 41226 * @type Number 41227 * @default null 41228 */ 41229 41230 41231 /** 41232 * @description Array of the absolute x and y positions of the Menu. This 41233 * property is only applied when the "position" configuration property is 41234 * set to dynamic. 41235 * @config xy 41236 * @type Number[] 41237 * @default null 41238 */ 41239 41240 41241 /** 41242 * @config context 41243 * @description Array of context arguments for context-sensitive positioning. 41244 * The format is: [id or element, element corner, context corner]. 41245 * For example, setting this property to ["img1", "tl", "bl"] would 41246 * align the Menu's top left corner to the context element's 41247 * bottom left corner. This property is only applied when the "position" 41248 * configuration property is set to dynamic. 41249 * @type Array 41250 * @default null 41251 */ 41252 41253 41254 /** 41255 * @config fixedcenter 41256 * @description Boolean indicating if the Menu should be anchored to the 41257 * center of the viewport. This property is only applied when the 41258 * "position" configuration property is set to dynamic. 41259 * @type Boolean 41260 * @default false 41261 */ 41262 41263 41264 /** 41265 * @config iframe 41266 * @description Boolean indicating whether or not the Menu should 41267 * have an IFRAME shim; used to prevent SELECT elements from 41268 * poking through an Overlay instance in IE6. When set to "true", 41269 * the iframe shim is created when the Menu instance is intially 41270 * made visible. This property is only applied when the "position" 41271 * configuration property is set to dynamic and is automatically applied 41272 * to all submenus. 41273 * @type Boolean 41274 * @default true for IE6 and below, false for all other browsers. 41275 */ 41276 41277 41278 // Add configuration attributes 41279 41280 /* 41281 Change the default value for the "visible" configuration 41282 property to "false" by re-adding the property. 41283 */ 41284 41285 /** 41286 * @config visible 41287 * @description Boolean indicating whether or not the menu is visible. If 41288 * the menu's "position" configuration property is set to "dynamic" (the 41289 * default), this property toggles the menu's <code><div></code> 41290 * element's "visibility" style property between "visible" (true) or 41291 * "hidden" (false). If the menu's "position" configuration property is 41292 * set to "static" this property toggles the menu's 41293 * <code><div></code> element's "display" style property 41294 * between "block" (true) or "none" (false). 41295 * @default false 41296 * @type Boolean 41297 */ 41298 oConfig.addProperty( 41299 VISIBLE_CONFIG.key, 41300 { 41301 handler: this.configVisible, 41302 value: VISIBLE_CONFIG.value, 41303 validator: VISIBLE_CONFIG.validator 41304 } 41305 ); 41306 41307 41308 /* 41309 Change the default value for the "constraintoviewport" configuration 41310 property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property. 41311 */ 41312 41313 /** 41314 * @config constraintoviewport 41315 * @description Boolean indicating if the menu will try to remain inside 41316 * the boundaries of the size of viewport. This property is only applied 41317 * when the "position" configuration property is set to dynamic and is 41318 * automatically applied to all submenus. 41319 * @default true 41320 * @type Boolean 41321 */ 41322 oConfig.addProperty( 41323 CONSTRAIN_TO_VIEWPORT_CONFIG.key, 41324 { 41325 handler: this.configConstrainToViewport, 41326 value: CONSTRAIN_TO_VIEWPORT_CONFIG.value, 41327 validator: CONSTRAIN_TO_VIEWPORT_CONFIG.validator, 41328 supercedes: CONSTRAIN_TO_VIEWPORT_CONFIG.supercedes 41329 } 41330 ); 41331 41332 41333 /* 41334 Change the default value for the "preventcontextoverlap" c
41334onfiguration 41335 property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property. 41336 */ 41337 41338 /** 41339 * @config preventcontextoverlap 41340 * @description Boolean indicating whether or not a submenu should overlap its parent MenuItem 41341 * when the "constraintoviewport" configuration property is set to "true". 41342 * @type Boolean 41343 * @default true 41344 */ 41345 oConfig.addProperty(PREVENT_CONTEXT_OVERLAP_CONFIG.key, { 41346 41347 value: PREVENT_CONTEXT_OVERLAP_CONFIG.value, 41348 validator: PREVENT_CONTEXT_OVERLAP_CONFIG.validator, 41349 supercedes: PREVENT_CONTEXT_OVERLAP_CONFIG.supercedes 41350 41351 }); 41352 41353 41354 /** 41355 * @config position 41356 * @description String indicating how a menu should be positioned on the 41357 * screen. Possible values are "static" and "dynamic." Static menus are 41358 * visible by default and reside in the normal flow of the document 41359 * (CSS position: static). Dynamic menus are hidden by default, reside 41360 * out of the normal flow of the document (CSS position: absolute), and 41361 * can overlay other elements on the screen. 41362 * @default dynamic
41363 * @type String 41364 */ 41365 oConfig.addProperty( 41366 POSITION_CONFIG.key, 41367 { 41368 handler: this.configPosition, 41369 value: POSITION_CONFIG.value, 41370 validator: POSITION_CONFIG.validator, 41371 supercedes: POSITION_CONFIG.supercedes 41372 } 41373 ); 41374 41375 41376 /** 41377 * @config submenualignment 41378 * @description Array defining how submenus should be aligned to their 41379 * parent menu item. The format is: [itemCorner, submenuCorner]. By default 41380 * a submenu's top left corner is aligned to its parent menu item's top 41381 * right corner. 41382 * @default ["tl","tr"] 41383 * @type Array 41384 */ 41385 oConfig.addProperty( 41386 SUBMENU_ALIGNMENT_CONFIG.key, 41387 { 41388 value: SUBMENU_ALIGNMENT_CONFIG.value, 41389 suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent 41390 } 41391 ); 41392 41393 41394 /** 41395 * @config autosubmenudisplay 41396 * @description Boolean indicating if submenus are automatically made 41397 * visible when the user mouses over the menu's items. 41398 * @default true 41399 * @type Boolean 41400 */ 41401 oConfig.addProperty( 41402 AUTO_SUBMENU_DISPLAY_CONFIG.key, 41403 { 41404 value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 41405 validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator, 41406 suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent 41407 } 41408 ); 41409 41410 41411 /** 41412 * @config showdelay 41413 * @description Number indicating the time (in milliseconds) that should 41414 * expire before a submenu is made visible when the user mouses over 41415 * the menu's items. This property is only applied when the "position" 41416 * configuration property is set to dynamic and is automatically applied 41417 * to all submenus. 41418 * @default 250 41419 * @type Number 41420 */ 41421 oConfig.addProperty( 41422 SHOW_DELAY_CONFIG.key, 41423 { 41424 value: SHOW_DELAY_CONFIG.value, 41425 validator: SHOW_DELAY_CONFIG.validator, 41426 suppressEvent: SHOW_DELAY_CONFIG.suppressEvent 41427 } 41428 ); 41429 41430 41431 /** 41432 * @config hidedelay 41433 * @description Number indicating the time (in milliseconds) that should 41434 * expire before the menu is hidden. This property is only applied when 41435 * the "position" configuration property is set to dynamic and is 41436 * automatically applied to all submenus. 41437 * @default 0 41438 * @type Number 41439 */ 41440 oConfig.addProperty( 41441 HIDE_DELAY_CONFIG.key, 41442 { 41443 handler: this.configHideDelay, 41444 value: HIDE_DELAY_CONFIG.value, 41445 validator: HIDE_DELAY_CONFIG.validator, 41446 suppressEvent: HIDE_DELAY_CONFIG.suppressEvent 41447 } 41448 ); 41449 41450 41451 /** 41452 * @config submenuhidedelay 41453 * @description Number indicating the time (in milliseconds) that should 41454 * expire before a submenu is hidden when the user mouses out of a menu item 41455 * heading in the direction of a submenu. The value must be greater than or 41456 * equal to the value specified for the "showdelay" configuration property. 41457 * This property is only applied when the "position" configuration property 41458 * is set to dynamic and is automatically applied to all submenus. 41459 * @default 250 41460 * @type Number 41461 */ 41462 oConfig.addProperty( 41463 SUBMENU_HIDE_DELAY_CONFIG.key, 41464 { 41465 value: SUBMENU_HIDE_DELAY_CONFIG.value, 41466 validator: SUBMENU_HIDE_DELAY_CONFIG.validator, 41467 suppressEvent: SUBMENU_HIDE_DELAY_CONFIG.suppressEvent 41468 } 41469 ); 41470 41471 41472 /** 41473 * @config clicktohide 41474 * @description Boolean indicating if the menu will automatically be 41475 * hidden if the user clicks outside of it. This property is only 41476 * applied when the "position" configuration property is set to dynamic 41477 * and is automatically applied to all submenus. 41478 * @default true 41479 * @type Boolean 41480 */ 41481 oConfig.addProperty( 41482 CLICK_TO_HIDE_CONFIG.key, 41483 { 41484 value: CLICK_TO_HIDE_CONFIG.value, 41485 validator: CLICK_TO_HIDE_CONFIG.validator, 41486 suppressEvent: CLICK_TO_HIDE_CONFIG.suppressEvent 41487 } 41488 ); 41489 41490 41491 /** 41492 * @config container 41493 * @description HTML element reference or string specifying the id 41494 * attribute of the HTML element that the menu's markup should be 41495 * rendered into. 41496 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 41497 * level-one-html.html#ID-58190037">HTMLElement</a>|String 41498 * @default document.body 41499 */ 41500 oConfig.addProperty( 41501 CONTAINER_CONFIG.key, 41502 { 41503 handler: this.configContainer, 41504 value: document.body, 41505 suppressEvent: CONTAINER_CONFIG.suppressEvent 41506 } 41507 ); 41508 41509 41510 /** 41511 * @config scrollincrement
41512 * @description Number used to control the scroll speed of a menu. Used to 41513 * increment the "scrollTop" property of the menu's body by when a menu's 41514 * content is scrolling. When set this property is automatically applied 41515 * to all submenus. 41516 * @default 1 41517 * @type Number 41518 */ 41519 oConfig.addProperty( 41520 SCROLL_INCREMENT_CONFIG.key, 41521 { 41522 value: SCROLL_INCREMENT_CONFIG.value, 41523 validator: SCROLL_INCREMENT_CONFIG.validator, 41524 supercedes: SCROLL_INCREMENT_CONFIG.supercedes, 41525 suppressEvent: SCROLL_INCREMENT_CONFIG.suppressEvent 41526 } 41527 ); 41528 41529 41530 /** 41531 * @config minscrollheight 41532 * @description Number defining the minimum threshold for the "maxheight" 41533 * configuration property. When set this property is automatically applied 41534 * to all submenus. 41535 * @default 90 41536 * @type Number 41537 */ 41538 oConfig.addProperty( 41539 MIN_SCROLL_HEIGHT_CONFIG.key, 41540 { 41541 value: MIN_SCROLL_HEIGHT_CONFIG.value, 41542 validator: MIN_SCROLL_HEIGHT_CONFIG.validator, 41543 supercedes: MIN_SCROLL_HEIGHT_CONFIG.supercedes, 41544 suppressEvent: MIN_SCROLL_HEIGHT_CONFIG.suppressEvent 41545 } 41546 ); 41547 41548 41549 /** 41550 * @config maxheight 41551 * @description Number defining the maximum height (in pixels) for a menu's 41552 * body element (<code><div class="bd"></code>). Once a menu's body 41553 * exceeds this height, the contents of the body are scrolled to maintain 41554 * this value. This value cannot be set lower than the value of the 41555 * "minscrollheight" configuration property. 41556 * @default 0 41557 * @type Number 41558 */ 41559 oConfig.addProperty( 41560 MAX_HEIGHT_CONFIG.key, 41561 { 41562 handler: this.configMaxHeight, 41563 value: MAX_HEIGHT_CONFIG.value, 41564 validator: MAX_HEIGHT_CONFIG.validator, 41565 suppressEvent: MAX_HEIGHT_CONFIG.suppressEvent, 41566 supercedes: MAX_HEIGHT_CONFIG.supercedes 41567 } 41568 ); 41569 41570 41571 /** 41572 * @config classname 41573 * @description String representing the CSS class to be applied to the 41574 * menu's root <code><div></code> element. The specified class(es) 41575 * are appended in addition to the default class as specified by the menu's 41576 * CSS_CLASS_NAME constant. When set this property is automatically 41577 * applied to all submenus. 41578 * @default null
41579 * @type String 41580 */ 41581 oConfig.addProperty( 41582 CLASS_NAME_CONFIG.key, 41583 { 41584 handler: this.configClassName, 41585 value: CLASS_NAME_CONFIG.value, 41586 validator: CLASS_NAME_CONFIG.validator, 41587 supercedes: CLASS_NAME_CONFIG.supercedes 41588 } 41589 ); 41590 41591 41592 /** 41593 * @config disabled 41594 * @description Boolean indicating if the menu should be disabled. 41595 * Disabling a menu disables each of its items. (Disabled menu items are 41596 * dimmed and will not respond to user input or fire events.) Disabled 41597 * menus have a corresponding "disabled" CSS class applied to their root 41598 * <code><div></code> element. 41599 * @default false 41600 * @type Boolean 41601 */ 41602 oConfig.addProperty( 41603 DISABLED_CONFIG.key, 41604 { 41605 handler: this.configDisabled, 41606 value: DISABLED_CONFIG.value, 41607 validator: DISABLED_CONFIG.validator, 41608 suppressEvent: DISABLED_CONFIG.suppressEvent 41609 } 41610 ); 41611 41612 41613 /** 41614 * @config shadow 41615 * @description Boolean indicating if the menu should have a shadow. 41616 * @default true 41617 * @type Boolean 41618 */ 41619 oConfig.addProperty( 41620 SHADOW_CONFIG.key, 41621 { 41622 handler: this.configShadow, 41623 value: SHADOW_CONFIG.value, 41624 validator: SHADOW_CONFIG.validator 41625 } 41626 ); 41627 41628 41629 /** 41630 * @config keepopen 41631 * @description Boolean indicating if the menu should remain open when clicked. 41632 * @default false 41633 * @type Boolean 41634 */ 41635 oConfig.addProperty( 41636 KEEP_OPEN_CONFIG.key, 41637 { 41638 value: KEEP_OPEN_CONFIG.value, 41639 validator: KEEP_OPEN_CONFIG.validator 41640 } 41641 ); 41642 41643} 41644 41645}); // END YAHOO.lang.extend 41646 41647})(); 41648 41649 41650 41651(function () { 41652 41653/** 41654* Creates an item for a menu. 41655* 41656* @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 41657* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 41658* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 41659* the <code><li></code> element of the menu item. 41660* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 41661* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 41662* specifying the <code><optgroup></code> element of the menu item. 41663* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 41664* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 41665* specifying the <code><option></code> element of the menu item. 41666* @param {Object} p_oConfig Optional. Object literal specifying the 41667* configuration for the menu item. See configuration class documentation 41668* for more details. 41669* @class MenuItem 41670* @constructor 41671*/ 41672YAHOO.widget.MenuItem = function (p_oObject, p_oConfig) { 41673 41674 if (p_oObject) { 41675 41676 if (p_oConfig) { 41677 41678 this.parent = p_oConfig.parent; 41679 this.value = p_oConfig.value; 41680 this.id = p_oConfig.id; 41681 41682 } 41683 41684 this.init(p_oObject, p_oConfig); 41685 41686 } 41687 41688}; 41689 41690 41691var Dom = YAHOO.util.Dom, 41692 Module = YAHOO.widget.Module, 41693 Menu = YAHOO.widget.Menu, 41694 MenuItem = YAHOO.widget.MenuItem, 41695 CustomEvent = YAHOO.util.CustomEvent, 41696 UA = YAHOO.env.ua, 41697 Lang = YAHOO.lang, 41698 41699 // Private string constants 41700 41701 _TEXT = "text", 41702 _HASH = "#", 41703 _HYPHEN = "-", 41704 _HELP_TEXT = "helptext", 41705 _URL = "url", 41706 _TARGET = "target", 41707 _EMPHASIS = "emphasis", 41708 _STRONG_EMPHASIS = "strongemphasis", 41709 _CHECKED = "checked", 41710 _SUBMENU = "submenu", 41711 _DISABLED = "disabled", 41712 _SELECTED = "selected", 41713 _HAS_SUBMENU = "hassubmenu", 41714 _CHECKED_DISABLED = "checked-disabled", 41715 _HAS_SUBMENU_DISABLED = "hassubmenu-disabled", 41716 _HAS_SUBMENU_SELECTED = "hassubmenu-selected", 41717 _CHECKED_SELECTED = "checked-selected", 41718 _ONCLICK = "onclick", 41719 _CLASSNAME = "classname", 41720 _EMPTY_STRING = "", 41721 _OPTION = "OPTION", 41722 _OPTGROUP = "OPTGROUP", 41723 _LI_UPPERCASE = "LI", 41724 _HREF = "href", 41725 _SELECT = "SELECT", 41726 _DIV = "DIV", 41727 _START_HELP_TEXT = "<em class=\"helptext\">", 41728 _START_EM = "<em>", 41729 _END_EM = "</em>", 41730 _START_STRONG = "<strong>", 41731 _END_STRONG = "</strong>", 41732 _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap", 41733 _OBJ = "obj", 41734 _SCOPE = "scope", 41735 _NONE = "none", 41736 _VISIBLE = "visible", 41737 _SPACE = " ", 41738 _MENUITEM = "MenuItem", 41739 _CLICK = "click", 41740 _SHOW = "show", 41741 _HIDE = "hide", 41742 _LI_LOWERCASE = "li", 41743 _ANCHOR_TEMPLATE = "<a href=\"#\"></a>", 41744 41745 EVENT_TYPES = [ 41746 41747 ["mouseOverEvent", "mouseover"], 41748 ["mouseOutEvent", "mouseout"], 41749 ["mouseDownEvent", "mousedown"], 41750 ["mouseUpEvent", "mouseup"], 41751 ["clickEvent", _CLICK], 41752 ["keyPressEvent", "keypress"], 41753 ["keyDownEvent", "keydown"], 41754 ["keyUpEvent", "keyup"],
41755 ["focusEvent", "focus"], 41756 ["blurEvent", "blur"], 41757 ["destroyEvent", "destroy"] 41758 41759 ], 41760 41761 TEXT_CONFIG = { 41762 key: _TEXT, 41763 value: _EMPTY_STRING, 41764 validator: Lang.isString, 41765 suppressEvent: true 41766 }, 41767 41768 HELP_TEXT_CONFIG = { 41769 key: _HELP_TEXT, 41770 supercedes: [_TEXT], 41771 suppressEvent: true 41772 }, 41773 41774 URL_CONFIG = { 41775 key: _URL, 41776 value: _HASH, 41777 suppressEvent: true 41778 }, 41779 41780 TARGET_CONFIG = { 41781 key: _TARGET, 41782 suppressEvent: true 41783 }, 41784 41785 EMPHASIS_CONFIG = { 41786 key: _EMPHASIS, 41787 value: false, 41788 validator: Lang.isBoolean, 41789 suppressEvent: true, 41790 supercedes: [_TEXT] 41791 }, 41792 41793 STRONG_EMPHASIS_CONFIG = { 41794 key: _STRONG_EMPHASIS, 41795 value: false, 41796 validator: Lang.isBoolean, 41797 suppressEvent: true, 41798 supercedes: [_TEXT] 41799 }, 41800 41801 CHECKED_CONFIG = { 41802 key: _CHECKED, 41803 value: false, 41804 validator: Lang.isBoolean, 41805 suppressEvent: true, 41806 supercedes: [_DISABLED, _SELECTED] 41807 }, 41808 41809 SUBMENU_CONFIG = { 41810 key: _SUBMENU, 41811 suppressEvent: true, 41812 supercedes: [_DISABLED, _SELECTED] 41813 }, 41814 41815 DISABLED_CONFIG = { 41816 key: _DISABLED, 41817 value: false, 41818 validator: Lang.isBoolean, 41819 suppressEvent: true, 41820 supercedes: [_TEXT, _SELECTED] 41821 }, 41822 41823 SELECTED_CONFIG = { 41824 key: _SELECTED, 41825 value: false, 41826 validator: Lang.isBoolean, 41827 suppressEvent: true 41828 }, 41829 41830 ONCLICK_CONFIG = { 41831 key: _ONCLICK, 41832 suppressEvent: true 41833 }, 41834 41835 CLASS_NAME_CONFIG = { 41836 key: _CLASSNAME, 41837 value: null, 41838 validator: Lang.isString, 41839 suppressEvent: true 41840 }, 41841 41842 KEY_LISTENER_CONFIG = { 41843 key: "keylistener", 41844 value: null, 41845 suppressEvent: true 41846 }, 41847 41848 m_oMenuItemTemplate = null, 41849 41850 CLASS_NAMES = {}; 41851 41852 41853/** 41854* @method getClassNameForState 41855* @description Returns a class name for the specified prefix and state. If the class name does not 41856* yet exist, it is created and stored in the CLASS_NAMES object to increase performance. 41857* @private 41858* @param {String} prefix String representing the prefix for the class name 41859* @param {String} state String representing a state - "disabled," "checked," etc. 41860*/ 41861var getClassNameForState = function (prefix, state) { 41862 41863 var oClassNames = CLASS_NAMES[prefix]; 41864 41865 if (!oClassNames) { 41866 CLASS_NAMES[prefix] = {}; 41867 oClassNames = CLASS_NAMES[prefix]; 41868 } 41869 41870 41871 var sClassName = oClassNames[state]; 41872 41873 if (!sClassName) { 41874 sClassName = prefix + _HYPHEN + state; 41875 oClassNames[state] = sClassName; 41876 } 41877 41878 return sClassName; 41879 41880}; 41881 41882 41883/** 41884* @method addClassNameForState 41885* @description Applies a class name to a MenuItem instance's <LI> and <A> elements 41886* that represents a MenuItem's state - "disabled," "checked," etc. 41887* @private 41888* @param {String} state String representing a state - "disabled," "checked," etc. 41889*/ 41890var addClassNameForState = function (state) { 41891 41892 Dom.addClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state)); 41893 Dom.addClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state)); 41894 41895}; 41896 41897/** 41898* @method removeClassNameForState 41899* @description Removes a class name from a MenuItem instance's <LI> and <A> elements 41900* that represents a MenuItem's state - "disabled," "checked," etc. 41901* @private 41902* @param {String} state String representing a state - "disabled," "checked," etc. 41903*/ 41904var removeClassNameForState = function (state) { 41905 41906 Dom.removeClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state)); 41907 Dom.removeClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state)); 41908 41909}; 41910 41911 41912MenuItem.prototype = { 41913 41914 /** 41915 * @property CSS_CLASS_NAME 41916 * @description String representing the CSS class(es) to be applied to the 41917 * <code><li></code> element of the menu item. 41918 * @default "yuimenuitem" 41919 * @final
41920 * @type String 41921 */ 41922 CSS_CLASS_NAME: "yuimenuitem", 41923 41924 41925 /** 41926 * @property CSS_LABEL_CLASS_NAME 41927 * @description String representing the CSS class(es) to be applied to the 41928 * menu item's <code><a></code> element. 41929 * @default "yuimenuitemlabel" 41930 * @final 41931 * @type String 41932 */ 41933 CSS_LABEL_CLASS_NAME: "yuimenuitemlabel", 41934 41935 41936 /** 41937 * @property SUBMENU_TYPE 41938 * @description Object representing the type of menu to instantiate and 41939 * add when parsing the child nodes of the menu item's source HTML element. 41940 * @final 41941 * @type YAHOO.widget.Menu 41942 */ 41943 SUBMENU_TYPE: null, 41944 41945 41946 41947 // Private member variables 41948 41949 41950 /** 41951 * @property _oAnchor 41952 * @description Object reference to the menu item's 41953 * <code><a></code> element. 41954 * @default null 41955 * @private 41956 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 41957 * one-html.html#ID-48250443">HTMLAnchorElement</a> 41958 */ 41959 _oAnchor: null, 41960 41961 41962 /** 41963 * @property _oHelpTextEM 41964 * @description Object reference to the menu item's help text 41965 * <code><em></code> element. 41966 * @default null 41967 * @private 41968 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 41969 * one-html.html#ID-58190037">HTMLElement</a> 41970 */ 41971 _oHelpTextEM: null, 41972 41973 41974 /** 41975 * @property _oSubmenu 41976 * @description Object reference to the menu item's submenu. 41977 * @default null 41978 * @private 41979 * @type YAHOO.widget.Menu 41980 */ 41981 _oSubmenu: null, 41982 41983 41984 /** 41985 * @property _oOnclickAttributeValue 41986 * @description Object reference to the menu item's current value for the 41987 * "onclick" configuration attribute. 41988 * @default null 41989 * @private 41990 * @type Object 41991 */ 41992 _oOnclickAttributeValue: null, 41993 41994 41995 /** 41996 * @property _sClassName 41997 * @description The current value of the "classname" configuration attribute. 41998 * @default null 41999 * @private
42000 * @type String 42001 */ 42002 _sClassName: null, 42003 42004 42005 42006 // Public properties 42007 42008 42009 /** 42010 * @property constructor 42011 * @description Object reference to the menu item's constructor function. 42012 * @default YAHOO.widget.MenuItem 42013 * @type YAHOO.widget.MenuItem 42014 */ 42015 constructor: MenuItem, 42016 42017 42018 /** 42019 * @property index 42020 * @description Number indicating the ordinal position of the menu item in 42021 * its group. 42022 * @default null 42023 * @type Number 42024 */ 42025 index: null, 42026 42027 42028 /** 42029 * @property groupIndex 42030 * @description Number indicating the index of the group to which the menu 42031 * item belongs. 42032 * @default null 42033 * @type Number 42034 */ 42035 groupIndex: null, 42036 42037 42038 /** 42039 * @property parent 42040 * @description Object reference to the menu item's parent menu. 42041 * @default null 42042 * @type YAHOO.widget.Menu 42043 */ 42044 parent: null, 42045 42046 42047 /** 42048 * @property element 42049 * @description Object reference to the menu item's 42050 * <code><li></code> element. 42051 * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level 42052 * -one-html.html#ID-74680021">HTMLLIElement</a> 42053 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 42054 * one-html.html#ID-74680021">HTMLLIElement</a> 42055 */ 42056 element: null, 42057 42058 42059 /** 42060 * @property srcElement 42061 * @description Object reference to the HTML element (either 42062 * <code><li></code>, <code><optgroup></code> or 42063 * <code><option></code>) used create the menu item. 42064 * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 42065 * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www. 42066 * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247" 42067 * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM- 42068 * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a> 42069 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 42070 * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3. 42071 * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"> 42072 * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM- 42073 * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a> 42074 */ 42075 srcElement: null, 42076 42077 42078 /** 42079 * @property value 42080 * @description Object reference to the menu item's value. 42081 * @default null 42082 * @type Object 42083 */ 42084 value: null, 42085 42086 42087 /** 42088 * @property browser 42089 * @deprecated Use YAHOO.env.ua 42090 * @description String representing the browser.
42091 * @type String 42092 */ 42093 browser: Module.prototype.browser, 42094 42095 42096 /** 42097 * @property id 42098 * @description Id of the menu item's root <code><li></code> 42099 * element. This property should be set via the constructor using the 42100 * configuration object literal. If an id is not specified, then one will 42101 * be created using the "generateId" method of the Dom utility. 42102 * @default null 42103 * @type String 42104 */ 42105 id: null, 42106 42107 42108 42109 // Events 42110 42111 42112 /** 42113 * @event destroyEvent 42114 * @description Fires when the menu item's <code><li></code> 42115 * element is removed from its parent <code><ul></code> element. 42116 * @type YAHOO.util.CustomEvent 42117 */ 42118 42119 42120 /** 42121 * @event mouseOverEvent 42122 * @description Fires when the mouse has entered the menu item. Passes 42123 * back the DOM Event object as an argument. 42124 * @type YAHOO.util.CustomEvent 42125 */ 42126 42127 42128 /** 42129 * @event mouseOutEvent 42130 * @description Fires when the mouse has left the menu item. Passes back 42131 * the DOM Event object as an argument. 42132 * @type YAHOO.util.CustomEvent 42133 */ 42134 42135 42136 /** 42137 * @event mouseDownEvent 42138 * @description Fires when the user mouses down on the menu item. Passes 42139 * back the DOM Event object as an argument. 42140 * @type YAHOO.util.CustomEvent 42141 */ 42142 42143 42144 /** 42145 * @event mouseUpEvent 42146 * @description Fires when the user releases a mouse button while the mouse 42147 * is over the menu item. Passes back the DOM Event object as an argument. 42148 * @type YAHOO.util.CustomEvent 42149 */ 42150 42151 42152 /** 42153 * @event clickEvent 42154 * @description Fires when the user clicks the on the menu item. Passes 42155 * back the DOM Event object as an argument. 42156 * @type YAHOO.util.CustomEvent 42157 */ 42158 42159 42160 /** 42161 * @event keyPressEvent 42162 * @description Fires when the user presses an alphanumeric key when the 42163 * menu item has focus. Passes back the DOM Event object as an argument. 42164 * @type YAHOO.util.CustomEvent 42165 */ 42166 42167 42168 /** 42169 * @event keyDownEvent 42170 * @description Fires when the user presses a key when the menu item has 42171 * focus. Passes back the DOM Event object as an argument. 42172 * @type YAHOO.util.CustomEvent 42173 */ 42174 42175 42176 /** 42177 * @event keyUpEvent 42178 * @description Fires when the user releases a key when the menu item has 42179 * focus. Passes back the DOM Event object as an argument. 42180 * @type YAHOO.util.CustomEvent 42181 */ 42182 42183 42184 /** 42185 * @event focusEvent 42186 * @description Fires when the menu item receives focus. 42187 * @type YAHOO.util.CustomEvent 42188 */ 42189 42190 42191 /** 42192 * @event blurEvent 42193 * @description Fires when the menu item loses the input focus. 42194 * @type YAHOO.util.CustomEvent 42195 */ 42196 42197 42198 /** 42199 * @method init 42200 * @description The MenuItem class's initialization method. This method is 42201 * automatically called by the constructor, and sets up all DOM references 42202 * for pre-existing markup, and creates required markup if it is not 42203 * already present. 42204 * @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 42205 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 42206 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 42207 * the <code><li></code> element of the menu item. 42208 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 42209 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 42210 * specifying the <code><optgroup></code> element of the menu item. 42211 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 42212 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 42213 * specifying the <code><option></code> element of the menu item. 42214 * @param {Object} p_oConfig Optional. Object literal specifying the 42215 * configuration for the menu item. See configuration class documentation 42216 * for more details. 42217 */ 42218 init: function (p_oObject, p_oConfig) { 42219 42220 42221 if (!this.SUBMENU_TYPE) { 42222 42223 this.SUBMENU_TYPE = Menu; 42224 42225 } 42226 42227 42228 // Create the config object 42229 42230 this.cfg = new YAHOO.util.Config(this); 42231 42232 this.initDefaultConfig(); 42233 42234 var oConfig = this.cfg, 42235 sURL = _HASH, 42236 oCustomEvent, 42237 aEventData, 42238 oAnchor, 42239 sTarget, 42240 sText, 42241 sId, 42242 i; 42243 42244 42245 if (Lang.isString(p_oObject)) { 42246 42247 this._createRootNodeStructure(); 42248 42249 oConfig.queueProperty(_TEXT, p_oObject); 42250 42251 } 42252 else if (p_oObject && p_oObject.tagName) { 42253 42254 switch(p_oObject.tagName.toUpperCase()) { 42255 42256 case _OPTION: 42257 42258 this._createRootNodeStructure(); 42259 42260 oConfig.queueProperty(_TEXT, p_oObject.text); 42261 oConfig.queueProperty(_DISABLED, p_oObject.disabled); 42262 42263 this.value = p_oObject.value; 42264 42265 this.srcElement = p_oObject; 42266 42267 break; 42268 42269 case _OPTGROUP: 42270 42271 this._createRootNodeStructure(); 42272 42273 oConfig.queueProperty(_TEXT, p_oObject.label); 42274 oConfig.queueProperty(_DISABLED, p_oObject.disabled); 42275 42276 this.srcElement = p_oObject; 42277 42278 this._initSubTree(); 42279 42280 break; 42281 42282 case _LI_UPPERCASE: 42283
42284 // Get the anchor node (if it exists) 42285 42286 oAnchor = Dom.getFirstChild(p_oObject); 42287 42288 42289 // Capture the "text" and/or the "URL" 42290 42291 if (oAnchor) { 42292 42293 sURL = oAnchor.getAttribute(_HREF, 2); 42294 sTarget = oAnchor.getAttribute(_TARGET); 42295 42296 sText = oAnchor.innerHTML; 42297 42298 } 42299 42300 this.srcElement = p_oObject; 42301 this.element = p_oObject; 42302 this._oAnchor = oAnchor; 42303 42304 /* 42305 Set these properties silently to sync up the 42306 configuration object without making changes to the 42307 element's DOM 42308 */ 42309 42310 oConfig.setProperty(_TEXT, sText, true); 42311 oConfig.setProperty(_URL, sURL, true); 42312 oConfig.setProperty(_TARGET, sTarget, true); 42313 42314 this._initSubTree(); 42315 42316 break; 42317 42318 } 42319 42320 } 42321 42322 42323 if (this.element) { 42324 42325 sId = (this.srcElement || this.element).id; 42326 42327 if (!sId) { 42328 42329 sId = this.id || Dom.generateId(); 42330 42331 this.element.id = sId; 42332 42333 } 42334 42335 this.id = sId; 42336 42337 42338 Dom.addClass(this.element, this.CSS_CLASS_NAME); 42339 Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME); 42340 42341 42342 i = EVENT_TYPES.length - 1; 42343 42344 do { 42345 42346 aEventData = EVENT_TYPES[i]; 42347 42348 oCustomEvent = this.createEvent(aEventData[1]); 42349 oCustomEvent.signature = CustomEvent.LIST; 42350 42351 this[aEventData[0]] = oCustomEvent; 42352 42353 } 42354 while (i--); 42355 42356 42357 if (p_oConfig) { 42358 42359 oConfig.applyConfig(p_oConfig); 42360 42361 } 42362 42363 oConfig.fireQueue(); 42364 42365 } 42366 42367 }, 42368 42369 42370 42371 // Private methods 42372 42373 /** 42374 * @method _createRootNodeStructure 42375 * @description Creates the core DOM structure for the menu item. 42376 * @private 42377 */ 42378 _createRootNodeStructure: function () { 42379 42380 var oElement, 42381 oAnchor; 42382 42383 if (!m_oMenuItemTemplate) { 42384 42385 m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE); 42386 m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE; 42387 42388 } 42389 42390 oElement = m_oMenuItemTemplate.cloneNode(true); 42391 oElement.className = this.CSS_CLASS_NAME; 42392 42393 oAnchor = oElement.firstChild; 42394 oAnchor.className = this.CSS_LABEL_CLASS_NAME; 42395 42396 this.element = oElement; 42397 this._oAnchor = oAnchor; 42398 42399 }, 42400 42401 42402 /** 42403 * @method _initSubTree 42404 * @description Iterates the source element's childNodes collection and uses 42405 * the child nodes to instantiate other menus. 42406 * @private 42407 */ 42408 _initSubTree: function () { 42409 42410 var oSrcEl = this.srcElement, 42411 oConfig = this.cfg, 42412 oNode, 42413 aOptions, 42414 nOptions, 42415 oMenu, 42416 n; 42417 42418 42419 if (oSrcEl.childNodes.length > 0) { 42420 42421 if (this.parent.lazyLoad && this.parent.srcElement && 42422 this.parent.srcElement.tagName.toUpperCase() == _SELECT) { 42423 42424 oConfig.setProperty( 42425 _SUBMENU, 42426 { id: Dom.generateId(), itemdata: oSrcEl.childNodes } 42427 ); 42428 42429 } 42430 else { 42431 42432 oNode = oSrcEl.firstChild; 42433 aOptions = []; 42434 42435 do { 42436 42437 if (oNode && oNode.tagName) { 42438 42439 switch(oNode.tagName.toUpperCase()) { 42440 42441 case _DIV: 42442 42443 oConfig.setProperty(_SUBMENU, oNode); 42444 42445 break; 42446 42447 case _OPTION: 42448 42449 aOptions[aOptions.length] = oNode; 42450 42451 break; 42452 42453 } 42454 42455 } 42456 42457 } 42458 while((oNode = oNode.nextSibling)); 42459 42460 42461 nOptions = aOptions.length; 42462 42463 if (nOptions > 0) { 42464 42465 oMenu = new this.SUBMENU_TYPE(Dom.generateId()); 42466 42467 oConfig.setProperty(_SUBMENU, oMenu); 42468 42469 for(n=0; n<nOptions; n++) { 42470 42471 oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n]))); 42472 42473 } 42474 42475 } 42476 42477 } 42478 42479 } 42480 42481 }, 42482 42483 42484 42485 // Event handlers for configuration properties 42486 42487 42488 /** 42489 * @method configText
42490 * @description Event handler for when the "text" configuration property of 42491 * the menu item changes. 42492 * @param {String} p_sType String representing the name of the event that 42493 * was fired. 42494 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42495 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42496 * that fired the event. 42497 */ 42498 configText: function (p_sType, p_aArgs, p_oItem) { 42499 42500 var sText = p_aArgs[0], 42501 oConfig = this.cfg, 42502 oAnchor = this._oAnchor, 42503 sHelpText = oConfig.getProperty(_HELP_TEXT), 42504 sHelpTextHTML = _EMPTY_STRING, 42505 sEmphasisStartTag = _EMPTY_STRING, 42506 sEmphasisEndTag = _EMPTY_STRING; 42507 42508 42509 if (sText) { 42510 42511 42512 if (sHelpText) { 42513 42514 sHelpTextHTML = _START_HELP_TEXT + sHelpText + _END_EM; 42515 42516 } 42517 42518 42519 if (oConfig.getProperty(_EMPHASIS)) { 42520 42521 sEmphasisStartTag = _START_EM; 42522 sEmphasisEndTag = _END_EM; 42523 42524 } 42525 42526 42527 if (oConfig.getProperty(_STRONG_EMPHASIS)) { 42528 42529 sEmphasisStartTag = _START_STRONG; 42530 sEmphasisEndTag = _END_STRONG; 42531 42532 } 42533 42534 42535 oAnchor.innerHTML = (sEmphasisStartTag + sText + sEmphasisEndTag + sHelpTextHTML); 42536 42537 } 42538 42539 }, 42540 42541 42542 /** 42543 * @method configHelpText 42544 * @description Event handler for when the "helptext" configuration property 42545 * of the menu item changes. 42546 * @param {String} p_sType String representing the name of the event that 42547 * was fired. 42548 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42549 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42550 * that fired the event. 42551 */ 42552 configHelpText: function (p_sType, p_aArgs, p_oItem) { 42553 42554 this.cfg.refireEvent(_TEXT); 42555 42556 }, 42557 42558 42559 /** 42560 * @method configURL 42561 * @description Event handler for when the "url" configuration property of 42562 * the menu item changes. 42563 * @param {String} p_sType String representing the name of the event that 42564 * was fired. 42565 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42566 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42567 * that fired the event. 42568 */ 42569 configURL: function (p_sType, p_aArgs, p_oItem) { 42570 42571 var sURL = p_aArgs[0]; 42572 42573 if (!sURL) { 42574 42575 sURL = _HASH; 42576 42577 } 42578 42579 var oAnchor = this._oAnchor; 42580 42581 if (UA.opera) { 42582 42583 oAnchor.removeAttribute(_HREF); 42584 42585 } 42586 42587 oAnchor.setAttribute(_HREF, sURL); 42588 42589 }, 42590 42591 42592 /** 42593 * @method configTarget 42594 * @description Event handler for when the "target" configuration property 42595 * of the menu item changes. 42596 * @param {String} p_sType String representing the name of the event that 42597 * was fired. 42598 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42599 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42600 * that fired the event. 42601 */ 42602 configTarget: function (p_sType, p_aArgs, p_oItem) { 42603 42604 var sTarget = p_aArgs[0], 42605 oAnchor = this._oAnchor; 42606 42607 if (sTarget && sTarget.length > 0) { 42608 42609 oAnchor.setAttribute(_TARGET, sTarget); 42610 42611 } 42612 else { 42613 42614 oAnchor.removeAttribute(_TARGET); 42615 42616 } 42617 42618 }, 42619 42620 42621 /** 42622 * @method configEmphasis 42623 * @description Event handler for when the "emphasis" configuration property 42624 * of the menu item changes. 42625 * @param {String} p_sType String representing the name of the event that 42626 * was fired. 42627 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42628 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42629 * that fired the event. 42630 */ 42631 configEmphasis: function (p_sType, p_aArgs, p_oItem) { 42632 42633 var bEmphasis = p_aArgs[0], 42634 oConfig = this.cfg; 42635 42636 42637 if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) { 42638 42639 oConfig.setProperty(_STRONG_EMPHASIS, false); 42640 42641 } 42642 42643 42644 oConfig.refireEvent(_TEXT); 42645 42646 }, 42647 42648 42649 /** 42650 * @method configStrongEmphasis
42651 * @description Event handler for when the "strongemphasis" configuration 42652 * property of the menu item changes. 42653 * @param {String} p_sType String representing the name of the event that 42654 * was fired. 42655 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42656 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42657 * that fired the event. 42658 */ 42659 configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) { 42660 42661 var bStrongEmphasis = p_aArgs[0], 42662 oConfig = this.cfg; 42663 42664 42665 if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) { 42666 42667 oConfig.setProperty(_EMPHASIS, false); 42668 42669 } 42670 42671 oConfig.refireEvent(_TEXT); 42672 42673 }, 42674 42675 42676 /** 42677 * @method configChecked 42678 * @description Event handler for when the "checked" configuration property 42679 * of the menu item changes. 42680 * @param {String} p_sType String representing the name of the event that 42681 * was fired. 42682 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42683 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42684 * that fired the event. 42685 */ 42686 configChecked: function (p_sType, p_aArgs, p_oItem) { 42687 42688 var bChecked = p_aArgs[0], 42689 oConfig = this.cfg; 42690 42691 42692 if (bChecked) { 42693 42694 addClassNameForState.call(this, _CHECKED); 42695 42696 } 42697 else { 42698 42699 removeClassNameForState.call(this, _CHECKED); 42700 } 42701 42702 42703 oConfig.refireEvent(_TEXT); 42704 42705 42706 if (oConfig.getProperty(_DISABLED)) { 42707 42708 oConfig.refireEvent(_DISABLED); 42709 42710 } 42711 42712 42713 if (oConfig.getProperty(_SELECTED)) { 42714 42715 oConfig.refireEvent(_SELECTED); 42716 42717 } 42718 42719 }, 42720 42721 42722 42723 /** 42724 * @method configDisabled 42725 * @description Event handler for when the "disabled" configuration property 42726 * of the menu item changes. 42727 * @param {String} p_sType String representing the name of the event that 42728 * was fired. 42729 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42730 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42731 * that fired the event. 42732 */ 42733 configDisabled: function (p_sType, p_aArgs, p_oItem) { 42734 42735 var bDisabled = p_aArgs[0], 42736 oConfig = this.cfg, 42737 oSubmenu = oConfig.getProperty(_SUBMENU), 42738 bChecked = oConfig.getProperty(_CHECKED); 42739 42740 42741 if (bDisabled) { 42742 42743 if (oConfig.getProperty(_SELECTED)) { 42744 42745 oConfig.setProperty(_SELECTED, false); 42746 42747 } 42748 42749 42750 addClassNameForState.call(this, _DISABLED); 42751 42752 42753 if (oSubmenu) { 42754 42755 addClassNameForState.call(this, _HAS_SUBMENU_DISABLED); 42756 42757 } 42758 42759 42760 if (bChecked) { 42761 42762 addClassNameForState.call(this, _CHECKED_DISABLED); 42763 42764 } 42765 42766 } 42767 else { 42768 42769 removeClassNameForState.call(this, _DISABLED); 42770 42771 42772 if (oSubmenu) { 42773 42774 removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED); 42775 42776 } 42777 42778 42779 if (bChecked) { 42780 42781 removeClassNameForState.call(this, _CHECKED_DISABLED); 42782 42783 } 42784 42785 } 42786 42787 }, 42788 42789 42790 /** 42791 * @method configSelected 42792 * @description Event handler for when the "selected" configuration property 42793 * of the menu item changes. 42794 * @param {String} p_sType String representing the name of the event that 42795 * was fired. 42796 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42797 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42798 * that fired the event. 42799 */ 42800 configSelected: function (p_sType, p_aArgs, p_oItem) { 42801 42802 var oConfig = this.cfg, 42803 oAnchor = this._oAnchor, 42804
42805 bSelected = p_aArgs[0], 42806 bChecked = oConfig.getProperty(_CHECKED), 42807 oSubmenu = oConfig.getProperty(_SUBMENU); 42808 42809 42810 if (UA.opera) { 42811 42812 oAnchor.blur(); 42813 42814 } 42815 42816 42817 if (bSelected && !oConfig.getProperty(_DISABLED)) { 42818 42819 addClassNameForState.call(this, _SELECTED); 42820 42821 42822 if (oSubmenu) { 42823 42824 addClassNameForState.call(this, _HAS_SUBMENU_SELECTED); 42825 42826 } 42827 42828 42829 if (bChecked) { 42830 42831 addClassNameForState.call(this, _CHECKED_SELECTED); 42832 42833 } 42834 42835 } 42836 else { 42837 42838 removeClassNameForState.call(this, _SELECTED); 42839 42840 42841 if (oSubmenu) { 42842 42843 removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED); 42844 42845 } 42846 42847 42848 if (bChecked) { 42849 42850 removeClassNameForState.call(this, _CHECKED_SELECTED); 42851 42852 } 42853 42854 } 42855 42856 42857 if (this.hasFocus() && UA.opera) { 42858 42859 oAnchor.focus(); 42860 42861 } 42862 42863 }, 42864 42865 42866 /** 42867 * @method _onSubmenuBeforeHide 42868 * @description "beforehide" Custom Event handler for a submenu. 42869 * @private 42870 * @param {String} p_sType String representing the name of the event that 42871 * was fired. 42872 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42873 */ 42874 _onSubmenuBeforeHide: function (p_sType, p_aArgs) { 42875 42876 var oItem = this.parent, 42877 oMenu; 42878 42879 function onHide() { 42880 42881 oItem._oAnchor.blur(); 42882 oMenu.beforeHideEvent.unsubscribe(onHide); 42883 42884 } 42885 42886 42887 if (oItem.hasFocus()) { 42888 42889 oMenu = oItem.parent; 42890 42891 oMenu.beforeHideEvent.subscribe(onHide); 42892 42893 } 42894 42895 }, 42896 42897 42898 /** 42899 * @method configSubmenu 42900 * @description Event handler for when the "submenu" configuration property 42901 * of the menu item changes. 42902 * @param {String} p_sType String representing the name of the event that 42903 * was fired. 42904 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 42905 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 42906 * that fired the event. 42907 */ 42908 configSubmenu: function (p_sType, p_aArgs, p_oItem) { 42909 42910 var oSubmenu = p_aArgs[0], 42911 oConfig = this.cfg, 42912 bLazyLoad = this.parent && this.parent.lazyLoad, 42913 oMenu, 42914 sSubmenuId, 42915 oSubmenuConfig; 42916 42917 42918 if (oSubmenu) { 42919 42920 if (oSubmenu instanceof Menu) { 42921 42922 oMenu = oSubmenu; 42923 oMenu.parent = this; 42924 oMenu.lazyLoad = bLazyLoad; 42925 42926 } 42927 else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) { 42928 42929 sSubmenuId = oSubmenu.id; 42930 oSubmenuConfig = oSubmenu; 42931 42932 oSubmenuConfig.lazyload = bLazyLoad; 42933 oSubmenuConfig.parent = this; 42934 42935 oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig); 42936 42937 42938 // Set the value of the property to the Menu instance 42939 42940 oConfig.setProperty(_SUBMENU, oMenu, true); 42941 42942 } 42943 else { 42944 42945 oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this }); 42946 42947 42948 // Set the value of the property to the Menu instance 42949 42950 oConfig.setProperty(_SUBMENU, oMenu, true); 42951 42952 } 42953 42954 42955 if (oMenu) { 42956 42957 oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true); 42958 42959 addClassNameForState.call(this, _HAS_SUBMENU); 42960 42961 42962 if (oConfig.getProperty(_URL) === _HASH) { 42963 42964 oConfig.setProperty(_URL, (_HASH + oMenu.id)); 42965 42966 } 42967 42968 42969 this._oSubmenu = oMenu; 42970 42971 42972 if (UA.opera) { 42973 42974 oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide); 42975 42976 } 42977 42978 } 42979 42980 } 42981 else { 42982 42983 removeClassNameForState.call(this, _HAS_SUBMENU); 42984 42985 if (this._oSubmenu) { 42986 42987 this._oSubmenu.destroy(); 42988 42989 } 42990 42991 } 42992 42993 42994 if (oConfig.getProperty(_DISABLED)) { 42995 42996 oConfig.refireEvent(_DISABLED); 42997 42998 } 42999 43000 43001 if (oConfig.getProperty(_SELECTED)) { 43002 43003 oConfig.refireEvent(_SELECTED); 43004 43005 } 43006 43007 }, 43008 43009 43010 /** 43011 * @method configOnClick
43012 * @description Event handler for when the "onclick" configuration property 43013 * of the menu item changes. 43014 * @param {String} p_sType String representing the name of the event that 43015 * was fired. 43016 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 43017 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 43018 * that fired the event. 43019 */ 43020 configOnClick: function (p_sType, p_aArgs, p_oItem) { 43021 43022 var oObject = p_aArgs[0]; 43023 43024 /* 43025 Remove any existing listeners if a "click" event handler has 43026 already been specified. 43027 */ 43028 43029 if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) { 43030 43031 this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn, 43032 this._oOnclickAttributeValue.obj); 43033 43034 this._oOnclickAttributeValue = null; 43035 43036 } 43037 43038 43039 if (!this._oOnclickAttributeValue && Lang.isObject(oObject) && 43040 Lang.isFunction(oObject.fn)) { 43041 43042 this.clickEvent.subscribe(oObject.fn, 43043 ((_OBJ in oObject) ? oObject.obj : this), 43044 ((_SCOPE in oObject) ? oObject.scope : null) ); 43045 43046 this._oOnclickAttributeValue = oObject; 43047 43048 } 43049 43050 }, 43051 43052 43053 /** 43054 * @method configClassName 43055 * @description Event handler for when the "classname" configuration 43056 * property of a menu item changes. 43057 * @param {String} p_sType String representing the name of the event that 43058 * was fired. 43059 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 43060 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 43061 * that fired the event. 43062 */ 43063 configClassName: function (p_sType, p_aArgs, p_oItem) { 43064 43065 var sClassName = p_aArgs[0]; 43066 43067 if (this._sClassName) { 43068 43069 Dom.removeClass(this.element, this._sClassName); 43070 43071 } 43072 43073 Dom.addClass(this.element, sClassName); 43074 this._sClassName = sClassName; 43075 43076 }, 43077 43078 43079 /** 43080 * @method _dispatchClickEvent 43081 * @description Dispatches a DOM "click" event to the anchor element of a 43082 * MenuItem instance. 43083 * @private 43084 */ 43085 _dispatchClickEvent: function () { 43086 43087 var oMenuItem = this, 43088 oAnchor; 43089 43090 if (!oMenuItem.cfg.getProperty(_DISABLED)) { 43091 oAnchor = Dom.getFirstChild(oMenuItem.element); 43092 43093 // Dispatch a "click" event to the MenuItem's anchor so that its 43094 // "click" event handlers will get called in response to the user 43095 // pressing the keyboard shortcut defined by the "keylistener" 43096 // configuration property. 43097 43098 this._dispatchDOMClick(oAnchor); 43099 } 43100 }, 43101 43102 /** 43103 * Utility method to dispatch a DOM click event on the HTMLElement passed in 43104 * 43105 * @method _dispatchDOMClick 43106 * @protected 43107 * @param {HTMLElement} el 43108 */ 43109 _dispatchDOMClick : function(el) { 43110 var oEvent; 43111 43112 // Choose the standards path for IE9 43113 if (UA.ie && UA.ie < 9) { 43114 el.fireEvent(_ONCLICK); 43115 } else { 43116 if ((UA.gecko && UA.gecko >= 1.9) || UA.opera || UA.webkit) { 43117 oEvent = document.createEvent("HTMLEvents"); 43118 oEvent.initEvent(_CLICK, true, true); 43119 } else { 43120 oEvent = document.createEvent("MouseEvents"); 43121 oEvent.initMouseEvent(_CLICK, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); 43122 } 43123 el.dispatchEvent(oEvent); 43124 } 43125 }, 43126 43127 /** 43128 * @method _createKeyListener 43129 * @description "show" event handler for a Menu instance - responsible for 43130 * setting up the KeyListener instance for a MenuItem. 43131 * @private 43132 * @param {String}
43132 type String representing the name of the event that 43133 * was fired. 43134 * @param {Array} args Array of arguments sent when the event was fired. 43135 * @param {Array} keyData Array of arguments sent when the event was fired. 43136 */ 43137 _createKeyListener: function (type, args, keyData) { 43138 43139 var oMenuItem = this, 43140 oMenu = oMenuItem.parent; 43141 43142 var oKeyListener = new YAHOO.util.KeyListener( 43143 oMenu.element.ownerDocument, 43144 keyData, 43145 { 43146 fn: oMenuItem._dispatchClickEvent, 43147 scope: oMenuItem, 43148 correctScope: true }); 43149 43150 43151 if (oMenu.cfg.getProperty(_VISIBLE)) { 43152 oKeyListener.enable(); 43153 } 43154 43155 43156 oMenu.subscribe(_SHOW, oKeyListener.enable, null, oKeyListener); 43157 oMenu.subscribe(_HIDE, oKeyListener.disable, null, oKeyListener); 43158 43159 oMenuItem._keyListener = oKeyListener; 43160 43161 oMenu.unsubscribe(_SHOW, oMenuItem._createKeyListener, keyData); 43162 43163 }, 43164 43165 43166 /** 43167 * @method configKeyListener 43168 * @description Event handler for when the "keylistener" configuration 43169 * property of a menu item changes. 43170 * @param {String} p_sType String representing the name of the event that 43171 * was fired. 43172 * @param {Array} p_aArgs Array of arguments sent when the event was fired. 43173 */ 43174 configKeyListener: function (p_sType, p_aArgs) { 43175 43176 var oKeyData = p_aArgs[0], 43177 oMenuItem = this, 43178 oMenu = oMenuItem.parent; 43179 43180 if (oMenuItem._keyData) { 43181 43182 // Unsubscribe from the "show" event in case the keylistener 43183 // config was changed before the Menu was ever made visible. 43184 43185 oMenu.unsubscribe(_SHOW, 43186 oMenuItem._createKeyListener, oMenuItem._keyData); 43187 43188 oMenuItem._keyData = null; 43189 43190 } 43191 43192 43193 // Tear down for the previous value of the "keylistener" property 43194 43195 if (oMenuItem._keyListener) { 43196 43197 oMenu.unsubscribe(_SHOW, oMenuItem._keyListener.enable); 43198 oMenu.unsubscribe(_HIDE, oMenuItem._keyListener.disable); 43199 43200 oMenuItem._keyListener.disable(); 43201 oMenuItem._keyListener = null; 43202 43203 } 43204 43205 43206 if (oKeyData) { 43207 43208 oMenuItem._keyData = oKeyData; 43209 43210 // Defer the creation of the KeyListener instance until the 43211 // parent Menu is visible. This is necessary since the 43212 // KeyListener instance needs to be bound to the document the 43213 // Menu has been rendered into. Deferring creation of the 43214 // KeyListener instance also improves performance. 43215 43216 oMenu.subscribe(_SHOW, oMenuItem._createKeyListener, 43217 oKeyData, oMenuItem); 43218 } 43219 43220 }, 43221 43222 43223 // Public methods 43224 43225 43226 /** 43227 * @method initDefaultConfig 43228 * @description Initializes an item's configurable properties. 43229 */ 43230 initDefaultConfig : function () { 43231 43232 var oConfig = this.cfg; 43233 43234 43235 // Define the configuration attributes 43236 43237 /** 43238 * @config text 43239 * @description String or markup specifying the text label for the menu item. 43240 * When building a menu from existing HTML the value of this property 43241 * will be interpreted from the menu's markup. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 43242 * @default "" 43243 * @type HTML 43244 */ 43245 oConfig.addProperty( 43246 TEXT_CONFIG.key, 43247 { 43248 handler: this.configText, 43249 value: TEXT_CONFIG.value, 43250 validator: TEXT_CONFIG.validator, 43251 suppressEvent: TEXT_CONFIG.suppressEvent 43252 } 43253 ); 43254 43255 43256 /** 43257 * @config helptext 43258 * @description String or markup specifying additional instructional text to 43259 * accompany the text for the menu item. The helptext is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 43260 * @deprecated Use "text" configuration property to add help text markup. 43261 * For example: <code>oMenuItem.cfg.setProperty("text", "Copy <em 43262 * class=\"helptext\">Ctrl + C</em>");</code> 43263 * @default null 43264 * @type HTML|<a href="http://www.w3.org/TR/ 43265 * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037"> 43266 * HTMLElement</a> 43267 */ 43268 oConfig.addProperty( 43269 HELP_TEXT_CONFIG.key, 43270 { 43271 handler: this.configHelpText, 43272 supercedes: HELP_TEXT_CONFIG.supercedes, 43273 suppressEvent: HELP_TEXT_CONFIG.suppressEvent 43274 } 43275 ); 43276 43277 43278 /** 43279 * @config url
43280 * @description String specifying the URL for the menu item's anchor's 43281 * "href" attribute. When building a menu from existing HTML the value 43282 * of this property will be interpreted from the menu's markup. Markup for the menu item content. The url is inserted into the DOM as an attribute value, and should be escaped by the implementor if coming from an external source. 43283 * @default "#" 43284 * @type String 43285 */ 43286 oConfig.addProperty( 43287 URL_CONFIG.key, 43288 { 43289 handler: this.configURL, 43290 value: URL_CONFIG.value, 43291 suppressEvent: URL_CONFIG.suppressEvent 43292 } 43293 ); 43294 43295 43296 /** 43297 * @config target 43298 * @description String specifying the value for the "target" attribute 43299 * of the menu item's anchor element. <strong>Specifying a target will 43300 * require the user to click directly on the menu item's anchor node in 43301 * order to cause the browser to navigate to the specified URL.</strong> 43302 * When building a menu from existing HTML the value of this property 43303 * will be interpreted from the menu's markup. The target is inserted into the DOM as an attribute value, and should be escaped by the implementor if coming from an external source. 43304 * @default null
43305 * @type String 43306 */ 43307 oConfig.addProperty( 43308 TARGET_CONFIG.key, 43309 { 43310 handler: this.configTarget, 43311 suppressEvent: TARGET_CONFIG.suppressEvent 43312 } 43313 ); 43314 43315 43316 /** 43317 * @config emphasis 43318 * @description Boolean indicating if the text of the menu item will be 43319 * rendered with emphasis. 43320 * @deprecated Use the "text" configuration property to add emphasis. 43321 * For example: <code>oMenuItem.cfg.setProperty("text", "<em>Some 43322 * Text</em>");</code> 43323 * @default false 43324 * @type Boolean 43325 */ 43326 oConfig.addProperty( 43327 EMPHASIS_CONFIG.key, 43328 { 43329 handler: this.configEmphasis, 43330 value: EMPHASIS_CONFIG.value, 43331 validator: EMPHASIS_CONFIG.validator, 43332 suppressEvent: EMPHASIS_CONFIG.suppressEvent, 43333 supercedes: EMPHASIS_CONFIG.supercedes 43334 } 43335 ); 43336 43337 43338 /** 43339 * @config strongemphasis 43340 * @description Boolean indicating if the text of the menu item will be 43341 * rendered with strong emphasis. 43342 * @deprecated Use the "text" configuration property to add strong emphasis. 43343 * For example: <code>oMenuItem.cfg.setProperty("text", "<strong> 43344 * Some Text</strong>");</code> 43345 * @default false 43346 * @type Boolean 43347 */ 43348 oConfig.addProperty( 43349 STRONG_EMPHASIS_CONFIG.key, 43350 { 43351 handler: this.configStrongEmphasis, 43352 value: STRONG_EMPHASIS_CONFIG.value, 43353 validator: STRONG_EMPHASIS_CONFIG.validator, 43354 suppressEvent: STRONG_EMPHASIS_CONFIG.suppressEvent, 43355 supercedes: STRONG_EMPHASIS_CONFIG.supercedes 43356 } 43357 ); 43358 43359 43360 /** 43361 * @config checked 43362 * @description Boolean indicating if the menu item should be rendered 43363 * with a checkmark. 43364 * @default false 43365 * @type Boolean 43366 */ 43367 oConfig.addProperty( 43368 CHECKED_CONFIG.key, 43369 { 43370 handler: this.configChecked, 43371 value: CHECKED_CONFIG.value, 43372 validator: CHECKED_CONFIG.validator, 43373 suppressEvent: CHECKED_CONFIG.suppressEvent, 43374 supercedes: CHECKED_CONFIG.supercedes 43375 } 43376 ); 43377 43378 43379 /** 43380 * @config disabled 43381 * @description Boolean indicating if the menu item should be disabled. 43382 * (Disabled menu items are dimmed and will not respond to user input 43383 * or fire events.) 43384 * @default false 43385 * @type Boolean 43386 */ 43387 oConfig.addProperty( 43388 DISABLED_CONFIG.key, 43389 { 43390 handler: this.configDisabled, 43391 value: DISABLED_CONFIG.value, 43392 validator: DISABLED_CONFIG.validator, 43393 suppressEvent: DISABLED_CONFIG.suppressEvent 43394 } 43395 ); 43396 43397 43398 /** 43399 * @config selected 43400 * @description Boolean indicating if the menu item should 43401 * be highlighted. 43402 * @default false 43403 * @type Boolean 43404 */ 43405 oConfig.addProperty( 43406 SELECTED_CONFIG.key, 43407 { 43408 handler: this.configSelected, 43409 value: SELECTED_CONFIG.value, 43410 validator: SELECTED_CONFIG.validator, 43411 suppressEvent: SELECTED_CONFIG.suppressEvent 43412 } 43413 ); 43414 43415 43416 /** 43417 * @config submenu 43418 * @description Object specifying the submenu to be appended to the 43419 * menu item. The value can be one of the following: <ul><li>Object 43420 * specifying a Menu instance.</li><li>Object literal specifying the 43421 * menu to be created. Format: <code>{ id: [menu id], itemdata: 43422 * [<a href="YAHOO.widget.Menu.html#itemData">array of values for 43423 * items</a>] }</code>.</li><li>String specifying the id attribute 43424 * of the <code><div></code> element of the menu.</li><li> 43425 * Object specifying the <code><div></code> element of the 43426 * menu.</li></ul> 43427 * @default null 43428 * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/ 43429 * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037"> 43430 * HTMLElement</a> 43431 */ 43432 oConfig.addProperty( 43433 SUBMENU_CONFIG.key, 43434 { 43435 handler: this.configSubmenu, 43436 supercedes: SUBMENU_CONFIG.supercedes, 43437 suppressEvent: SUBMENU_CONFIG.suppressEvent 43438 } 43439 ); 43440 43441 43442 /** 43443 * @config onclick
43444 * @description Object literal representing the code to be executed when 43445 * the item is clicked. Format:<br> <code> {<br> 43446 * <strong>fn:</strong> Function, // The handler to call when 43447 * the event fires.<br> <strong>obj:</strong> Object, // An 43448 * object to pass back to the handler.<br> <strong>scope:</strong> 43449 * Object // The object to use for the scope of the handler. 43450 * <br> } </code> 43451 * @type Object 43452 * @default null 43453 */ 43454 oConfig.addProperty( 43455 ONCLICK_CONFIG.key, 43456 { 43457 handler: this.configOnClick, 43458 suppressEvent: ONCLICK_CONFIG.suppressEvent 43459 } 43460 ); 43461 43462 43463 /** 43464 * @config classname 43465 * @description CSS class to be applied to the menu item's root 43466 * <code><li></code> element. The specified class(es) are 43467 * appended in addition to the default class as specified by the menu 43468 * item's CSS_CLASS_NAME constant. 43469 * @default null
43470 * @type String 43471 */ 43472 oConfig.addProperty( 43473 CLASS_NAME_CONFIG.key, 43474 { 43475 handler: this.configClassName, 43476 value: CLASS_NAME_CONFIG.value, 43477 validator: CLASS_NAME_CONFIG.validator, 43478 suppressEvent: CLASS_NAME_CONFIG.suppressEvent 43479 } 43480 ); 43481 43482 43483 /** 43484 * @config keylistener 43485 * @description Object literal representing the key(s) that can be used 43486 * to trigger the MenuItem's "click" event. Possible attributes are 43487 * shift (boolean), alt (boolean), ctrl (boolean) and keys (either an int 43488 * or an array of ints representing keycodes). 43489 * @default null 43490 * @type Object 43491 */ 43492 oConfig.addProperty( 43493 KEY_LISTENER_CONFIG.key, 43494 { 43495 handler: this.configKeyListener, 43496 value: KEY_LISTENER_CONFIG.value, 43497 suppressEvent: KEY_LISTENER_CONFIG.suppressEvent 43498 } 43499 ); 43500 43501 }, 43502 43503 /** 43504 * @method getNextSibling 43505 * @description Finds the menu item's next sibling. 43506 * @return YAHOO.widget.MenuItem 43507 */ 43508 getNextSibling: function () { 43509 43510 var isUL = function (el) { 43511 return (el.nodeName.toLowerCase() === "ul"); 43512 }, 43513 43514 menuitemEl = this.element, 43515 next = Dom.getNextSibling(menuitemEl), 43516 parent, 43517 sibling, 43518 list; 43519 43520 if (!next) { 43521 43522 parent = menuitemEl.parentNode; 43523 sibling = Dom.getNextSiblingBy(parent, isUL); 43524 43525 if (sibling) { 43526 list = sibling; 43527 } 43528 else { 43529 list = Dom.getFirstChildBy(parent.parentNode, isUL); 43530 } 43531 43532 next = Dom.getFirstChild(list); 43533 43534 } 43535 43536 return YAHOO.widget.MenuManager.getMenuItem(next.id); 43537 43538 }, 43539 43540 /** 43541 * @method getNextEnabledSibling 43542 * @description Finds the menu item's next enabled sibling. 43543 * @return YAHOO.widget.MenuItem 43544 */ 43545 getNextEnabledSibling: function () { 43546 43547 var next = this.getNextSibling(); 43548 43549 return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getNextEnabledSibling() : next; 43550 43551 }, 43552 43553 43554 /** 43555 * @method getPreviousSibling 43556 * @description Finds the menu item's previous sibling. 43557 * @return {YAHOO.widget.MenuItem} 43558 */ 43559 getPreviousSibling: function () { 43560 43561 var isUL = function (el) { 43562 return (el.nodeName.toLowerCase() === "ul"); 43563 }, 43564 43565 menuitemEl = this.element, 43566 next = Dom.getPreviousSibling(menuitemEl), 43567 parent, 43568 sibling, 43569 list; 43570 43571 if (!next) { 43572 43573 parent = menuitemEl.parentNode; 43574 sibling = Dom.getPreviousSiblingBy(parent, isUL); 43575 43576 if (sibling) { 43577 list = sibling; 43578 } 43579 else { 43580 list = Dom.getLastChildBy(parent.parentNode, isUL); 43581 } 43582 43583 next = Dom.getLastChild(list); 43584 43585 } 43586 43587 return YAHOO.widget.MenuManager.getMenuItem(next.id); 43588 43589 }, 43590 43591 43592 /** 43593 * @method getPreviousEnabledSibling 43594 * @description Finds the menu item's previous enabled sibling. 43595 * @return {YAHOO.widget.MenuItem} 43596 */ 43597 getPreviousEnabledSibling: function () { 43598 43599 var next = this.getPreviousSibling(); 43600 43601 return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getPreviousEnabledSibling() : next; 43602 43603 }, 43604 43605 43606 /** 43607 * @method focus 43608 * @description Causes the menu item to receive the focus and fires the 43609 * focus event. 43610 */ 43611 focus: function () { 43612 43613 var oParent = this.parent, 43614 oAnchor = this._oAnchor, 43615 oActiveItem = oParent.activeItem; 43616 43617 43618 function setFocus() { 43619 43620 try { 43621 43622 if (!(UA.ie && !document.hasFocus())) { 43623 43624 if (oActiveItem) { 43625 43626 oActiveItem.blurEvent.fire(); 43627 43628 } 43629 43630 oAnchor.focus(); 43631 43632 this.focusEvent.fire(); 43633 43634 } 43635 43636 } 43637 catch(e) { 43638 43639 } 43640 43641 } 43642 43643 43644 if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) && 43645 this.element.style.display != _NONE) { 43646 43647 43648 /* 43649 Setting focus via a timer fixes a race condition in Firefox, IE 43650 and Opera where the browser viewport jumps as it trys to 43651 position and focus the menu. 43652 */ 43653 43654 Lang.later(0, this, setFocus); 43655 43656 } 43657 43658 }, 43659 43660 43661 /** 43662 * @method blur
43663 * @description Causes the menu item to lose focus and fires the 43664 * blur event. 43665 */ 43666 blur: function () { 43667 43668 var oParent = this.parent; 43669 43670 if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) { 43671 43672 Lang.later(0, this, function () { 43673 43674 try { 43675 43676 this._oAnchor.blur(); 43677 this.blurEvent.fire(); 43678 43679 } 43680 catch (e) { 43681 43682 } 43683 43684 }, 0); 43685 43686 } 43687 43688 }, 43689 43690 43691 /** 43692 * @method hasFocus 43693 * @description Returns a boolean indicating whether or not the menu item 43694 * has focus. 43695 * @return {Boolean} 43696 */ 43697 hasFocus: function () { 43698 43699 return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this); 43700 43701 }, 43702 43703 43704 /** 43705 * @method destroy 43706 * @description Removes the menu item's <code><li></code> element 43707 * from its parent <code><ul></code> element. 43708 */ 43709 destroy: function () { 43710 43711 var oEl = this.element, 43712 oSubmenu, 43713 oParentNode, 43714 aEventData, 43715 i; 43716 43717 43718 if (oEl) { 43719 43720 43721 // If the item has a submenu, destroy it first 43722 43723 oSubmenu = this.cfg.getProperty(_SUBMENU); 43724 43725 if (oSubmenu) { 43726 43727 oSubmenu.destroy(); 43728 43729 } 43730 43731 43732 // Remove the element from the parent node 43733 43734 oParentNode = oEl.parentNode; 43735 43736 if (oParentNode) { 43737 43738 oParentNode.removeChild(oEl); 43739 43740 this.destroyEvent.fire(); 43741 43742 } 43743 43744 43745 // Remove CustomEvent listeners 43746 43747 i = EVENT_TYPES.length - 1; 43748 43749 do { 43750 43751 aEventData = EVENT_TYPES[i]; 43752 43753 this[aEventData[0]].unsubscribeAll(); 43754 43755 } 43756 while (i--); 43757 43758 43759 this.cfg.configChangedEvent.unsubscribeAll(); 43760 43761 } 43762 43763 }, 43764 43765 43766 /** 43767 * @method toString 43768 * @description Returns a string representing the menu item. 43769 * @return {String} 43770 */ 43771 toString: function () { 43772 43773 var sReturnVal = _MENUITEM, 43774 sId = this.id; 43775 43776 if (sId) { 43777 43778 sReturnVal += (_SPACE + sId); 43779 43780 } 43781 43782 return sReturnVal; 43783 43784 } 43785 43786}; 43787 43788Lang.augmentProto(MenuItem, YAHOO.util.EventProvider); 43789 43790})(); 43791(function () { 43792 43793 var _XY = "xy", 43794 _MOUSEDOWN = "mousedown", 43795 _CONTEXTMENU = "ContextMenu", 43796 _SPACE = " "; 43797 43798/** 43799* Creates a list of options or commands which are made visible in response to 43800* an HTML element's "contextmenu" event ("mousedown" for Opera). 43801* 43802* @param {String} p_oElement String specifying the id attribute of the 43803* <code><div></code> element of the context menu. 43804* @param {String} p_oElement String specifying the id attribute of the 43805* <code><select></code> element to be used as the data source for the 43806* context menu. 43807* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one- 43808* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 43809* <code><div></code> element of the context menu. 43810* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one- 43811* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 43812* the <code><select></code> element to be used as the data source for 43813* the context menu. 43814* @param {Object} p_oConfig Optional. Object literal specifying the 43815* configuration for the context menu. See configuration class documentation 43816* for more details. 43817* @class ContextMenu 43818* @constructor 43819* @extends YAHOO.widget.Menu 43820* @namespace YAHOO.widget 43821*/ 43822YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) { 43823 YAHOO.widget.ContextMenu.superclass.constructor.call(this, p_oElement, p_oConfig); 43824}; 43825 43826 43827var Event = YAHOO.util.Event, 43828 UA = YAHOO.env.ua, 43829 ContextMenu = YAHOO.widget.ContextMenu, 43830 43831 43832 43833 /** 43834 * Constant representing the name of the ContextMenu's events 43835 * @property EVENT_TYPES 43836 * @private 43837 * @final 43838 * @type Object 43839 */ 43840 EVENT_TYPES = { 43841 43842 "TRIGGER_CONTEXT_MENU": "triggerContextMenu", 43843 "CONTEXT_MENU": (UA.opera ? _MOUSEDOWN : "contextmenu"), 43844 "CLICK": "click" 43845 43846 }, 43847 43848 43849 /** 43850 * Constant representing the ContextMenu's configuration properties 43851 * @property DEFAULT_CONFIG 43852 * @private 43853 * @final 43854 * @type Object 43855 */ 43856 TRIGGER_CONFIG = { 43857 key: "trigger", 43858 suppressEvent: true 43859 }; 43860 43861 43862/** 43863* @method position 43864* @description "beforeShow" event handler used to position the contextmenu. 43865* @private 43866* @param {String}
43866 p_sType String representing the name of the event that 43867* was fired. 43868* @param {Array} p_aArgs Array of arguments sent when the event was fired. 43869* @param {Array} p_aPos Array representing the xy position for the context menu. 43870*/ 43871function position(p_sType, p_aArgs, p_aPos) { 43872 this.cfg.setProperty(_XY, p_aPos); 43873 this.beforeShowEvent.unsubscribe(position, p_aPos); 43874} 43875 43876 43877YAHOO.lang.extend(ContextMenu, YAHOO.widget.Menu, { 43878 43879 43880 43881// Private properties 43882 43883 43884/** 43885* @property _oTrigger 43886* @description Object reference to the current value of the "trigger" 43887* configuration property. 43888* @default null 43889* @private 43890* @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/leve 43891* l-one-html.html#ID-58190037">HTMLElement</a>|Array 43892*/ 43893_oTrigger: null, 43894 43895 43896/** 43897* @property _bCancelled 43898* @description Boolean indicating if the display of the context menu should 43899* be cancelled. 43900* @default false 43901* @private 43902* @type Boolean 43903*/ 43904_bCancelled: false, 43905 43906 43907 43908// Public properties 43909 43910 43911/** 43912* @property contextEventTarget 43913* @description Object reference for the HTML element that was the target of the 43914* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of 43915* the context menu. 43916* @default null 43917* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one- 43918* html.html#ID-58190037">HTMLElement</a> 43919*/ 43920contextEventTarget: null, 43921 43922 43923 43924// Events 43925 43926 43927/** 43928* @event triggerContextMenuEvent 43929* @param type {String} The name of the event, "triggerContextMenu" 43930* @param args {Array} The array of event arguments. For this event, the underlying 43931* DOM event is the only argument, available from args[0]. 43932* @description Custom Event wrapper for the "contextmenu" DOM event
43933* ("mousedown" for Opera) fired by the element(s) that trigger the display of 43934* the context menu. 43935*/ 43936triggerContextMenuEvent: null, 43937 43938 43939 43940/** 43941* @method init 43942* @description The ContextMenu class's initialization method. This method is 43943* automatically called by the constructor, and sets up all DOM references for 43944* pre-existing markup, and creates required markup if it is not already present. 43945* @param {String} p_oElement String specifying the id attribute of the 43946* <code><div></code> element of the context menu. 43947* @param {String} p_oElement String specifying the id attribute of the 43948* <code><select></code> element to be used as the data source for 43949* the context menu. 43950* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one- 43951* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 43952* <code><div></code> element of the context menu. 43953* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one- 43954* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 43955* the <code><select></code> element to be used as the data source for 43956* the context menu. 43957* @param {Object} p_oConfig Optional. Object literal specifying the 43958* configuration for the context menu. See configuration class documentation 43959* for more details. 43960*/ 43961init: function(p_oElement, p_oConfig) { 43962 43963 43964 // Call the init of the superclass (YAHOO.widget.Menu) 43965 43966 ContextMenu.superclass.init.call(this, p_oElement); 43967 43968 this.beforeInitEvent.fire(ContextMenu); 43969 43970 if (p_oConfig) { 43971 this.cfg.applyConfig(p_oConfig, true); 43972 } 43973 43974 this.initEvent.fire(ContextMenu); 43975}, 43976 43977 43978/** 43979* @method initEvents 43980* @description Initializes the custom events for the context menu. 43981*/ 43982initEvents: function() { 43983 ContextMenu.superclass.initEvents.call(this); 43984 43985 // Create custom events 43986 this.triggerContextMenuEvent = this.createEvent(EVENT_TYPES.TRIGGER_CONTEXT_MENU); 43987 this.triggerContextMenuEvent.signature = YAHOO.util.CustomEvent.LIST; 43988}, 43989 43990/** 43991* @method cancel 43992* @description Cancels the display of the context menu. 43993*/ 43994cancel: function() { 43995 this._bCancelled = true; 43996}, 43997 43998// Private methods 43999 44000 44001/** 44002* @method _removeEventHandlers 44003* @description Removes all of the DOM event handlers from the HTML element(s) 44004* whose "context menu" event ("click" for Opera) trigger the display of 44005* the context menu. 44006* @private 44007*/ 44008_removeEventHandlers: function() { 44009 44010 var oTrigger = this._oTrigger; 44011 44012 // Remove the event handlers from the trigger(s) 44013 if (oTrigger) { 44014 Event.removeListener(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu); 44015 44016 if (UA.opera) { 44017 Event.removeListener(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick); 44018 } 44019 } 44020 44021}, 44022 44023// Private event handlers 44024 44025/** 44026* @method _onTriggerClick 44027* @description "click" event handler for the HTML element(s) identified as the 44028* "trigger" for the context menu. Used to cancel default behaviors in Opera. 44029* @private 44030* @param {Event} p_oEvent Object representing the DOM event object passed back 44031* by the event utility (YAHOO.util.Event). 44032* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 44033* menu that is handling the event. 44034*/ 44035_onTriggerClick: function(p_oEvent, p_oMenu) { 44036 44037 if (p_oEvent.ctrlKey) { 44038 Event.stopEvent(p_oEvent); 44039 } 44040 44041}, 44042 44043 44044/** 44045* @method _onTriggerContextMenu 44046* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML 44047* element(s) that trigger the display of the context menu. 44048* @private 44049* @param {Event} p_oEvent Object representing the DOM event object passed back 44050* by the event utility (YAHOO.util.Event). 44051* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 44052* menu that is handling the event. 44053*/ 44054_onTriggerContextMenu: function(p_oEvent, p_oMenu) { 44055 44056 var aXY; 44057 44058 if (!(p_oEvent.type == _MOUSEDOWN && !p_oEvent.ctrlKey)) { 44059 44060 this.contextEventTarget = Event.getTarget(p_oEvent); 44061 44062 this.triggerContextMenuEvent.fire(p_oEvent); 44063 44064 44065 if (!this._bCancelled) { 44066 44067 /* 44068 Prevent the browser's default context menu from appearing and 44069 stop the propagation of the "contextmenu" event so that 44070 other ContextMenu instances are not displayed. 44071 */ 44072 44073 Event.stopEvent(p_oEvent); 44074 44075 44076 // Hide any other Menu instances that might be visible 44077
44078 YAHOO.widget.MenuManager.hideVisible(); 44079 44080 44081 44082 // Position and display the context menu 44083 44084 aXY = Event.getXY(p_oEvent); 44085 44086 44087 if (!YAHOO.util.Dom.inDocument(this.element)) { 44088 44089 this.beforeShowEvent.subscribe(position, aXY); 44090 44091 } 44092 else { 44093 44094 this.cfg.setProperty(_XY, aXY); 44095 44096 } 44097 44098 44099 this.show(); 44100 44101 } 44102 44103 this._bCancelled = false; 44104 44105 } 44106 44107}, 44108 44109 44110 44111// Public methods 44112 44113 44114/** 44115* @method toString 44116* @description Returns a string representing the context menu. 44117* @return {String} 44118*/ 44119toString: function() { 44120 44121 var sReturnVal = _CONTEXTMENU, 44122 sId = this.id; 44123 44124 if (sId) { 44125 44126 sReturnVal += (_SPACE + sId); 44127 44128 } 44129 44130 return sReturnVal; 44131 44132}, 44133 44134 44135/** 44136* @method initDefaultConfig 44137* @description Initializes the class's configurable properties which can be 44138* changed using the context menu's Config object ("cfg"). 44139*/ 44140initDefaultConfig: function() { 44141 44142 ContextMenu.superclass.initDefaultConfig.call(this); 44143 44144 /** 44145 * @config trigger 44146 * @description The HTML element(s) whose "contextmenu" event ("mousedown" 44147 * for Opera) trigger the display of the context menu. Can be a string 44148 * representing the id attribute of the HTML element, an object reference 44149 * for the HTML element, or an array of strings or HTML element references. 44150 * @default null
44151 * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 44152 * level-one-html.html#ID-58190037">HTMLElement</a>|Array 44153 */ 44154 this.cfg.addProperty(TRIGGER_CONFIG.key, 44155 { 44156 handler: this.configTrigger, 44157 suppressEvent: TRIGGER_CONFIG.suppressEvent 44158 } 44159 ); 44160 44161}, 44162 44163 44164/** 44165* @method destroy 44166* @description Removes the context menu's <code><div></code> element 44167* (and accompanying child nodes) from the document. 44168* @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 44169* NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 44170*/ 44171destroy: function(shallowPurge) { 44172 44173 // Remove the DOM event handlers from the current trigger(s) 44174 44175 this._removeEventHandlers(); 44176 44177 44178 // Continue with the superclass implementation of this method 44179 44180 ContextMenu.superclass.destroy.call(this, shallowPurge); 44181 44182}, 44183 44184 44185 44186// Public event handlers for configuration properties 44187 44188 44189/** 44190* @method configTrigger 44191* @description Event handler for when the value of the "trigger" configuration 44192* property changes. 44193* @param {String} p_sType String representing the name of the event that 44194* was fired. 44195* @param {Array} p_aArgs Array of arguments sent when the event was fired. 44196* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 44197* menu that fired the event. 44198*/ 44199configTrigger: function(p_sType, p_aArgs, p_oMenu) { 44200 44201 var oTrigger = p_aArgs[0]; 44202 44203 if (oTrigger) { 44204 44205 /* 44206 If there is a current "trigger" - remove the event handlers 44207 from that element(s) before assigning new ones 44208 */ 44209 44210 if (this._oTrigger) { 44211 44212 this._removeEventHandlers(); 44213 44214 } 44215 44216 this._oTrigger = oTrigger; 44217 44218 44219 /* 44220 Listen for the "mousedown" event in Opera b/c it does not 44221 support the "contextmenu" event 44222 */ 44223 44224 Event.on(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu, this, true); 44225 44226 44227 /* 44228 Assign a "click" event handler to the trigger element(s) for 44229 Opera to prevent default browser behaviors. 44230 */ 44231 44232 if (UA.opera) { 44233 44234 Event.on(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick, this, true); 44235 44236 } 44237 44238 } 44239 else { 44240 44241 this._removeEventHandlers(); 44242 44243 } 44244 44245} 44246 44247}); // END YAHOO.lang.extend 44248 44249}()); 44250 44251 44252 44253/** 44254* Creates an item for a context menu. 44255* 44256* @param {String} p_oObject String specifying the text of the context menu item. 44257* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44258* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 44259* <code><li></code> element of the context menu item. 44260* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44261* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 44262* specifying the <code><optgroup></code> element of the context 44263* menu item. 44264* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44265* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 44266* the <code><option></code> element of the context menu item. 44267* @param {Object} p_oConfig Optional. Object literal specifying the 44268* configuration for the context menu item. See configuration class 44269* documentation for more details. 44270* @class ContextMenuItem 44271* @constructor 44272* @extends YAHOO.widget.MenuItem 44273* @deprecated As of version 2.4.0 items for YAHOO.widget.ContextMenu instances 44274* are of type YAHOO.widget.MenuItem. 44275*/ 44276YAHOO.widget.ContextMenuItem = YAHOO.widget.MenuItem; 44277(function () { 44278 44279 var Lang = YAHOO.lang, 44280 44281 // String constants 44282 44283 _STATIC = "static", 44284 _DYNAMIC_STATIC = "dynamic," + _STATIC, 44285 _DISABLED = "disabled", 44286 _SELECTED = "selected", 44287 _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay", 44288 _SUBMENU = "submenu", 44289 _VISIBLE = "visible", 44290 _SPACE = " ", 44291 _SUBMENU_TOGGLE_REGION = "submenutoggleregion", 44292 _MENUBAR = "MenuBar"; 44293 44294/** 44295* Horizontal collection of items, each of which can contain a submenu. 44296* 44297* @param {String} p_oElement String specifying the id attribute of the 44298* <code><div></code> element of the menu bar. 44299* @param {String} p_oElement String specifying the id attribute of the 44300* <code><select></code> element to be used as the data source for the 44301* menu bar. 44302* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44303* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 44304* the <code><div></code> element of the menu bar. 44305* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44306* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 44307* specifying the <code><select></code> element to be used as the data 44308* source for the menu bar. 44309* @param {Object} p_oConfig Optional. Object literal specifying the 44310* configuration for the menu bar. See configuration class documentation for 44311* more details. 44312* @class MenuBar 44313* @constructor
44314* @extends YAHOO.widget.Menu 44315* @namespace YAHOO.widget 44316*/ 44317YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) { 44318 44319 YAHOO.widget.MenuBar.superclass.constructor.call(this, p_oElement, p_oConfig); 44320 44321}; 44322 44323 44324/** 44325* @method checkPosition 44326* @description Checks to make sure that the value of the "position" property 44327* is one of the supported strings. Returns true if the position is supported. 44328* @private 44329* @param {Object} p_sPosition String specifying the position of the menu. 44330* @return {Boolean} 44331*/ 44332function checkPosition(p_sPosition) { 44333 44334 var returnVal = false; 44335 44336 if (Lang.isString(p_sPosition)) { 44337 44338 returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1); 44339 44340 } 44341 44342 return returnVal; 44343 44344} 44345 44346 44347var Event = YAHOO.util.Event, 44348 MenuBar = YAHOO.widget.MenuBar, 44349 44350 POSITION_CONFIG = { 44351 key: "position", 44352 value: _STATIC, 44353 validator: checkPosition, 44354 supercedes: [_VISIBLE] 44355 }, 44356 44357 SUBMENU_ALIGNMENT_CONFIG = { 44358 key: "submenualignment", 44359 value: ["tl","bl"] 44360 }, 44361 44362 AUTO_SUBMENU_DISPLAY_CONFIG = { 44363 key: _AUTO_SUBMENU_DISPLAY, 44364 value: false, 44365 validator: Lang.isBoolean, 44366 suppressEvent: true 44367 }, 44368 44369 SUBMENU_TOGGLE_REGION_CONFIG = { 44370 key: _SUBMENU_TOGGLE_REGION, 44371 value: false, 44372 validator: Lang.isBoolean 44373 }; 44374 44375 44376 44377Lang.extend(MenuBar, YAHOO.widget.Menu, { 44378 44379/** 44380* @method init 44381* @description The MenuBar class's initialization method. This method is 44382* automatically called by the constructor, and sets up all DOM references for 44383* pre-existing markup, and creates required markup if it is not already present. 44384* @param {String} p_oElement String specifying the id attribute of the 44385* <code><div></code> element of the menu bar. 44386* @param {String} p_oElement String specifying the id attribute of the 44387* <code><select></code> element to be used as the data source for the 44388* menu bar. 44389* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44390* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 44391* the <code><div></code> element of the menu bar. 44392* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44393* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 44394* specifying the <code><select></code> element to be used as the data 44395* source for the menu bar. 44396* @param {Object} p_oConfig Optional. Object literal specifying the 44397* configuration for the menu bar. See configuration class documentation for 44398* more details. 44399*/ 44400init: function(p_oElement, p_oConfig) { 44401 44402 if(!this.ITEM_TYPE) { 44403 44404 this.ITEM_TYPE = YAHOO.widget.MenuBarItem; 44405 44406 } 44407 44408 44409 // Call the init of the superclass (YAHOO.widget.Menu) 44410 44411 MenuBar.superclass.init.call(this, p_oElement); 44412 44413 44414 this.beforeInitEvent.fire(MenuBar); 44415 44416 44417 if(p_oConfig) { 44418 44419 this.cfg.applyConfig(p_oConfig, true); 44420 44421 } 44422 44423 this.initEvent.fire(MenuBar); 44424 44425}, 44426 44427 44428 44429// Constants 44430 44431 44432/** 44433* @property CSS_CLASS_NAME 44434* @description String representing the CSS class(es) to be applied to the menu 44435* bar's <code><div></code> element. 44436* @default "yuimenubar" 44437* @final 44438* @type String 44439*/ 44440CSS_CLASS_NAME: "yuimenubar", 44441 44442 44443/** 44444* @property SUBMENU_TOGGLE_REGION_WIDTH 44445* @description Width (in pixels) of the area of a MenuBarItem that, when pressed, will toggle the 44446* display of the MenuBarItem's submenu. 44447* @default 20 44448* @final 44449* @type Number 44450*/ 44451SUBMENU_TOGGLE_REGION_WIDTH: 20, 44452 44453 44454// Protected event handlers 44455 44456 44457/** 44458* @method _onKeyDown 44459* @description "keydown" Custom Event handler for the menu bar. 44460* @private 44461* @param {String} p_sType String representing the name of the event that 44462* was fired. 44463* @param {Array} p_aArgs Array of arguments sent when the event was fired. 44464* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 44465* that fired the event. 44466*/ 44467_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) { 44468 44469 var oEvent = p_aArgs[0], 44470 oItem = p_aArgs[1], 44471 oSubmenu, 44472 oItemCfg, 44473 oNextItem; 44474 44475 44476 if(oItem && !oItem.cfg.getProperty(_DISABLED)) { 44477 44478 oItemCfg = oItem.cfg; 44479 44480 switch(oEvent.keyCode) { 44481 44482 case 37: // Left arrow 44483 case 39: // Right arrow 44484 44485 if(oItem == this.activeItem && !oItemCfg.getProperty(_SELECTED)) { 44486 44487 oItemCfg.setProperty(_SELECTED, true); 44488 44489 } 44490 else { 44491 44492 oNextItem = (oEvent.keyCode == 37) ? 44493 oItem.getPreviousEnabledSibling() : 44494 oItem.getNextEnabledSibling(); 44495 44496 if(oNextItem) { 44497 44498 this.clearActiveItem(); 44499 44500 oNextItem.cfg.setProperty(_SELECTED, true); 44501 44502 oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); 44503 44504 if(oSubmenu) { 44505 44506 oSubmenu.show(); 44507 oSubmenu.setInitialFocus(); 44508 44509 } 44510 else { 44511 oNextItem.focus(); 44512 } 44513 44514 } 44515 44516 } 44517 44518 Event.preventDefault(oEvent); 44519 44520 break; 44521 44522 case 40: // Down arrow 44523 44524 if(this.activeItem != oItem) { 44525 44526 this.clearActiveItem(); 44527 44528 oItemCfg.setProperty(_SELECTED, true); 44529 oItem.focus(); 44530 44531 } 44532 44533 oSubmenu = oItemCfg.getProperty(_SUBMENU); 44534 44535 if(oSubmenu) { 44536 44537 if(oSubmenu.cfg.getProperty(_VISIBLE)) { 44538 44539 oSubmenu.setInitialSelection(); 44540 oSubmenu.setInitialFocus(); 44541 44542 } 44543 else { 44544 44545 oSubmenu.show(); 44546 oSubmenu.setInitialFocus(); 44547 44548 } 44549 44550 } 44551 44552 Event.preventDefault(oEvent); 44553 44554 break; 44555 44556 } 44557 44558 } 44559 44560 44561 if(oEvent.keyCode == 27 && this.activeItem) { // Esc key 44562 44563 oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU); 44564 44565 if(oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) { 44566 44567 oSubmenu.hide(); 44568 this.activeItem.focus(); 44569 44570 } 44571 else { 44572 44573 this.activeItem.cfg.setProperty(_SELECTED, false); 44574 this.activeItem.blur(); 44575 44576 } 44577 44578 Event.preventDefault(oEvent); 44579 44580 } 44581 44582}, 44583 44584 44585/** 44586* @method _onClick 44587* @description "click" event handler for the menu bar. 44588* @protected 44589* @param {String}
44589 p_sType String representing the name of the event that 44590* was fired. 44591* @param {Array} p_aArgs Array of arguments sent when the event was fired. 44592* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 44593* that fired the event. 44594*/ 44595_onClick: function(p_sType, p_aArgs, p_oMenuBar) { 44596 44597 MenuBar.superclass._onClick.call(this, p_sType, p_aArgs, p_oMenuBar); 44598 44599 var oItem = p_aArgs[1], 44600 bReturnVal = true, 44601 oItemEl, 44602 oEvent, 44603 oTarget, 44604 oActiveItem, 44605 oConfig, 44606 oSubmenu, 44607 nMenuItemX, 44608 nToggleRegion; 44609 44610 44611 var toggleSubmenuDisplay = function () { 44612 44613 if(oSubmenu.cfg.getProperty(_VISIBLE)) { 44614 44615 oSubmenu.hide(); 44616 44617 } 44618 else { 44619 44620 oSubmenu.show(); 44621 44622 } 44623 44624 }; 44625 44626 44627 if(oItem && !oItem.cfg.getProperty(_DISABLED)) { 44628 44629 oEvent = p_aArgs[0]; 44630 oTarget = Event.getTarget(oEvent); 44631 oActiveItem = this.activeItem; 44632 oConfig = this.cfg; 44633 44634 44635 // Hide any other submenus that might be visible 44636 44637 if(oActiveItem && oActiveItem != oItem) { 44638 44639 this.clearActiveItem(); 44640 44641 } 44642 44643 44644 oItem.cfg.setProperty(_SELECTED, true); 44645 44646 44647 // Show the submenu for the item 44648 44649 oSubmenu = oItem.cfg.getProperty(_SUBMENU); 44650 44651 44652 if(oSubmenu) { 44653 44654 oItemEl = oItem.element; 44655 nMenuItemX = YAHOO.util.Dom.getX(oItemEl); 44656 nToggleRegion = nMenuItemX + (oItemEl.offsetWidth - this.SUBMENU_TOGGLE_REGION_WIDTH); 44657 44658 if (oConfig.getProperty(_SUBMENU_TOGGLE_REGION)) { 44659 44660 if (Event.getPageX(oEvent) > nToggleRegion) { 44661 44662 toggleSubmenuDisplay(); 44663 44664 Event.preventDefault(oEvent); 44665 44666 /* 44667 Return false so that other click event handlers are not called when the 44668 user clicks inside the toggle region. 44669 */ 44670 bReturnVal = false;
44671 44672 } 44673 44674 } 44675 else { 44676 44677 toggleSubmenuDisplay(); 44678 44679 } 44680 44681 } 44682 44683 } 44684 44685 44686 return bReturnVal; 44687 44688}, 44689 44690 44691 44692// Public methods 44693 44694/** 44695* @method configSubmenuToggle 44696* @description Event handler for when the "submenutoggleregion" configuration property of 44697* a MenuBar changes. 44698* @param {String} p_sType The name of the event that was fired. 44699* @param {Array} p_aArgs Collection of arguments sent when the event was fired. 44700*/ 44701configSubmenuToggle: function (p_sType, p_aArgs) { 44702 44703 var bSubmenuToggle = p_aArgs[0]; 44704 44705 if (bSubmenuToggle) { 44706 44707 this.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false); 44708 44709 } 44710 44711}, 44712 44713 44714/** 44715* @method toString 44716* @description Returns a string representing the menu bar. 44717* @return {String} 44718*/ 44719toString: function() { 44720 44721 var sReturnVal = _MENUBAR, 44722 sId = this.id; 44723 44724 if(sId) { 44725 44726 sReturnVal += (_SPACE + sId); 44727 44728 } 44729 44730 return sReturnVal; 44731 44732}, 44733 44734 44735/** 44736* @description Initializes the class's configurable properties which can be 44737* changed using the menu bar's Config object ("cfg"). 44738* @method initDefaultConfig 44739*/ 44740initDefaultConfig: function() { 44741 44742 MenuBar.superclass.initDefaultConfig.call(this); 44743 44744 var oConfig = this.cfg; 44745 44746 // Add configuration properties 44747 44748 44749 /* 44750 Set the default value for the "position" configuration property 44751 to "static" by re-adding the property. 44752 */ 44753 44754 44755 /** 44756 * @config position 44757 * @description String indicating how a menu bar should be positioned on the 44758 * screen. Possible values are "static" and "dynamic." Static menu bars 44759 * are visible by default and reside in the normal flow of the document 44760 * (CSS position: static). Dynamic menu bars are hidden by default, reside 44761 * out of the normal flow of the document (CSS position: absolute), and can 44762 * overlay other elements on the screen. 44763 * @default static
44764 * @type String 44765 */ 44766 oConfig.addProperty( 44767 POSITION_CONFIG.key, 44768 { 44769 handler: this.configPosition, 44770 value: POSITION_CONFIG.value, 44771 validator: POSITION_CONFIG.validator, 44772 supercedes: POSITION_CONFIG.supercedes 44773 } 44774 ); 44775 44776 44777 /* 44778 Set the default value for the "submenualignment" configuration property 44779 to ["tl","bl"] by re-adding the property. 44780 */ 44781 44782 /** 44783 * @config submenualignment 44784 * @description Array defining how submenus should be aligned to their 44785 * parent menu bar item. The format is: [itemCorner, submenuCorner]. 44786 * @default ["tl","bl"] 44787 * @type Array 44788 */ 44789 oConfig.addProperty( 44790 SUBMENU_ALIGNMENT_CONFIG.key, 44791 { 44792 value: SUBMENU_ALIGNMENT_CONFIG.value, 44793 suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent 44794 } 44795 ); 44796 44797 44798 /* 44799 Change the default value for the "autosubmenudisplay" configuration 44800 property to "false" by re-adding the property. 44801 */ 44802 44803 /** 44804 * @config autosubmenudisplay 44805 * @description Boolean indicating if submenus are automatically made 44806 * visible when the user mouses over the menu bar's items. 44807 * @default false 44808 * @type Boolean 44809 */ 44810 oConfig.addProperty( 44811 AUTO_SUBMENU_DISPLAY_CONFIG.key, 44812 { 44813 value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 44814 validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator, 44815 suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent 44816 } 44817 ); 44818 44819 44820 /** 44821 * @config submenutoggleregion 44822 * @description Boolean indicating if only a specific region of a MenuBarItem should toggle the 44823 * display of a submenu. The default width of the region is determined by the value of the 44824 * SUBMENU_TOGGLE_REGION_WIDTH property. If set to true, the autosubmenudisplay 44825 * configuration property will be set to false, and any click event listeners will not be 44826 * called when the user clicks inside the submenu toggle region of a MenuBarItem. If the 44827 * user clicks outside of the submenu toggle region, the MenuBarItem will maintain its 44828 * standard behavior. 44829 * @default false 44830 * @type Boolean 44831 */ 44832 oConfig.addProperty( 44833 SUBMENU_TOGGLE_REGION_CONFIG.key, 44834 { 44835 value: SUBMENU_TOGGLE_REGION_CONFIG.value, 44836 validator: SUBMENU_TOGGLE_REGION_CONFIG.validator, 44837 handler: this.configSubmenuToggle 44838 } 44839 ); 44840 44841} 44842 44843}); // END YAHOO.lang.extend 44844 44845}()); 44846 44847 44848 44849/** 44850* Creates an item for a menu bar. 44851* 44852* @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 44853* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44854* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 44855* <code><li></code> element of the menu bar item. 44856* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44857* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 44858* specifying the <code><optgroup></code> element of the menu bar item. 44859* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44860* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 44861* the <code><option></code> element of the menu bar item. 44862* @param {Object} p_oConfig Optional. Object literal specifying the 44863* configuration for the menu bar item. See configuration class documentation 44864* for more details. 44865* @class MenuBarItem 44866* @constructor 44867* @extends YAHOO.widget.MenuItem 44868*/ 44869YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) { 44870 44871 YAHOO.widget.MenuBarItem.superclass.constructor.call(this, p_oObject, p_oConfig); 44872 44873}; 44874 44875YAHOO.lang.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, { 44876 44877 44878 44879/** 44880* @method init 44881* @description The MenuBarItem class's initialization method. This method is 44882* automatically called by the constructor, and sets up all DOM references for 44883* pre-existing markup, and creates required markup if it is not already present. 44884* @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 44885* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44886* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 44887* <code><li></code> element of the menu bar item. 44888* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44889* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 44890* specifying the <code><optgroup></code> element of the menu bar item. 44891* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level- 44892* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 44893* the <code><option></code> element of the menu bar item. 44894* @param {Object} p_oConfig Optional. Object literal specifying the 44895* configuration for the menu bar item. See configuration class documentation 44896* for more details. 44897*/ 44898init: function(p_oObject, p_oConfig) { 44899 44900 if(!this.SUBMENU_TYPE) { 44901
44902 this.SUBMENU_TYPE = YAHOO.widget.Menu; 44903 44904 } 44905 44906 44907 /* 44908 Call the init of the superclass (YAHOO.widget.MenuItem) 44909 Note: We don't pass the user config in here yet 44910 because we only want it executed once, at the lowest 44911 subclass level. 44912 */ 44913 44914 YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject); 44915 44916 44917 var oConfig = this.cfg; 44918 44919 if(p_oConfig) { 44920 44921 oConfig.applyConfig(p_oConfig, true); 44922 44923 } 44924 44925 oConfig.fireQueue(); 44926 44927}, 44928 44929 44930 44931// Constants 44932 44933 44934/** 44935* @property CSS_CLASS_NAME 44936* @description String representing the CSS class(es) to be applied to the 44937* <code><li></code> element of the menu bar item. 44938* @default "yuimenubaritem" 44939* @final 44940* @type String 44941*/ 44942CSS_CLASS_NAME: "yuimenubaritem", 44943 44944 44945/** 44946* @property CSS_LABEL_CLASS_NAME 44947* @description String representing the CSS class(es) to be applied to the 44948* menu bar item's <code><a></code> element. 44949* @default "yuimenubaritemlabel" 44950* @final 44951* @type String 44952*/ 44953CSS_LABEL_CLASS_NAME: "yuimenubaritemlabel", 44954 44955 44956 44957// Public methods 44958 44959 44960/** 44961* @method toString 44962* @description Returns a string representing the menu bar item. 44963* @return {String} 44964*/ 44965toString: function() { 44966 44967 var sReturnVal = "MenuBarItem"; 44968 44969 if(this.cfg && this.cfg.getProperty("text")) { 44970 44971 sReturnVal += (": " + this.cfg.getProperty("text")); 44972 44973 } 44974 44975 return sReturnVal; 44976 44977} 44978 44979}); // END YAHOO.lang.extend 44980YAHOO.register("menu", YAHOO.widget.Menu, {version: "2.9.0", build: "2800"}); 44981/* 44982Copyright (c) 2011, Yahoo! Inc. All rights reserved. 44983Code licensed under the BSD License: 44984http://developer.yahoo.com/yui/license.html 44985version: 2.9.0 44986*/ 44987/** 44988 * Provides methods to parse JSON strings and convert objects to JSON strings. 44989 * 44990 * @module json 44991 * @class JSON 44992 * @namespace YAHOO.lang
44993 * @static 44994 */ 44995(function () { 44996 44997var l = YAHOO.lang, 44998 isFunction = l.isFunction, 44999 isObject = l.isObject, 45000 isArray = l.isArray, 45001 _toStr = Object.prototype.toString, 45002 // 'this' is the global object. window in browser env. Keep 45003 // the code env agnostic. Caja requies window, unfortunately. 45004 Native = (YAHOO.env.ua.caja ? window : this).JSON, 45005 45006/* Variables used by parse */ 45007 45008 /** 45009 * Replace certain Unicode characters that JavaScript may handle incorrectly 45010 * during eval--either by deleting them or treating them as line 45011 * endings--with escape sequences. 45012 * IMPORTANT NOTE: This regex will be used to modify the input if a match is 45013 * found. 45014 * 45015 * @property _UNICODE_EXCEPTIONS 45016 * @type {RegExp} 45017 * @private 45018 */ 45019 _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 45020 45021 /** 45022 * First step in the safety evaluation. Regex used to replace all escape 45023 * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character). 45024 * 45025 * @property _ESCAPES 45026 * @type {RegExp} 45027 * @static 45028 * @private 45029 */ 45030 _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, 45031 45032 /** 45033 * Second step in the safety evaluation. Regex used to replace all simple 45034 * values with ']' characters. 45035 * 45036 * @property _VALUES 45037 * @type {RegExp} 45038 * @static 45039 * @private 45040 */ 45041 _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, 45042 45043 /** 45044 * Third step in the safety evaluation. Regex used to remove all open 45045 * square brackets following a colon, comma, or at the beginning of the 45046 * string. 45047 * 45048 * @property _BRACKETS 45049 * @type {RegExp} 45050 * @static 45051 * @private 45052 */ 45053 _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g, 45054 45055 /** 45056 * Final step in the safety evaluation. Regex used to test the string left 45057 * after all previous replacements for invalid characters. 45058 * 45059 * @property _UNSAFE 45060 * @type {RegExp} 45061 * @static 45062 * @private 45063 */ 45064 _UNSAFE = /[^\],:{}\s]/, 45065 45066 45067/* Variables used by stringify */ 45068 45069 /** 45070 * Regex used to replace special characters in strings for JSON 45071 * stringification. 45072 * 45073 * @property _SPECIAL_CHARS 45074 * @type {RegExp} 45075 * @static 45076 * @private 45077 */ 45078 _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 45079 45080 /** 45081 * Character substitution map for common escapes and special characters. 45082 * 45083 * @property _CHARS 45084 * @type {Object} 45085 * @static 45086 * @private 45087 */ 45088 _CHARS = { 45089 '\b': '\\b', 45090 '\t': '\\t', 45091 '\n': '\\n', 45092 '\f': '\\f', 45093 '\r': '\\r', 45094 '"' : '\\"', 45095 '\\': '\\\\' 45096 }, 45097 45098 UNDEFINED = 'undefined', 45099 OBJECT = 'object', 45100 NULL = 'null', 45101 STRING = 'string', 45102 NUMBER = 'number', 45103 BOOLEAN = 'boolean', 45104 DATE = 'date', 45105 _allowable = { 45106 'undefined' : UNDEFINED, 45107 'string' : STRING, 45108 '[object String]' : STRING, 45109 'number' : NUMBER, 45110 '[object Number]' : NUMBER, 45111 'boolean' : BOOLEAN, 45112 '[object Boolean]' : BOOLEAN, 45113 '[object Date]' : DATE, 45114 '[object RegExp]' : OBJECT 45115 }, 45116 EMPTY = '', 45117 OPEN_O = '{', 45118 CLOSE_O = '}', 45119 OPEN_A = '[', 45120 CLOSE_A = ']', 45121 COMMA = ',', 45122 COMMA_CR = ",\n", 45123 CR = "\n", 45124 COLON = ':', 45125 COLON_SP = ': ', 45126 QUOTE = '"'; 45127 45128// Only accept JSON objects that report a [[Class]] of JSON 45129Native = _toStr.call(Native) === '[object JSON]' && Native; 45130 45131// Escapes a special character to a safe Unicode representation 45132function _char(c) { 45133 if (!_CHARS[c]) { 45134 _CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4); 45135 } 45136 return _CHARS[c]; 45137} 45138 45139 45140/* functions used by parse */ 45141 45142/** 45143 * Traverses nested objects, applying a filter or reviver function to 45144 * each value. The value returned from the function will replace the 45145 * original value in the key:value pair. If the value returned is 45146 * undefined, the key will be omitted from the returned object. 45147 * 45148 * @method _revive 45149 * @param data {MIXED} Any JavaScript data 45150 * @param reviver {Function} filter or mutation function 45151 * @return {MIXED} The results of the filtered/mutated data structure 45152 * @private 45153 */ 45154function _revive(data, reviver) { 45155 var walk = function (o,key) { 45156 var k,v,value = o[key]; 45157 if (value && typeof value === 'object') { 45158 for (k in value) { 45159 if (l.hasOwnProperty(value,k)) { 45160 v = walk(value, k); 45161 if (v === undefined) { 45162 delete value[k]; 45163 } else { 45164 value[k] = v; 45165 } 45166 } 45167 } 45168 } 45169 return reviver.call(o,key,value); 45170 }; 45171 45172 return typeof reviver === 'function' ? walk({'':data},'') : data; 45173} 45174 45175/** 45176 * Replace certain Unicode characters that may be handled incorrectly by 45177 * some browser implementations. 45178 * 45179 * @method _prepare 45180 * @param s {String} parse input 45181 * @return {String} sanitized JSON string ready to be validated/parsed 45182 * @private 45183 */ 45184function _prepare(s) { 45185 return s.replace(_UNICODE_EXCEPTIONS, _char); 45186} 45187 45188function _isSafe(str) { 45189 return l.isString(str) && 45190 !_UNSAFE.test(str.replace(_ESCAPES,'@'). 45191 replace(_VALUES,']'). 45192 replace(_BRACKETS,'')); 45193} 45194 45195function _parse(s,reviver) { 45196 // sanitize 45197 s = _prepare(s); 45198
45199 // Ensure valid JSON 45200 if (_isSafe(s)) { 45201 // Eval the text into a JavaScript data structure, apply the 45202 // reviver function if provided, and return 45203 return _revive( eval('(' + s + ')'), reviver ); 45204 } 45205 45206 // The text is not valid JSON 45207 throw new SyntaxError('JSON.parse'); 45208} 45209 45210 45211 45212/* functions used by stringify */ 45213 45214// Utility function used to determine how to serialize a variable. 45215function _type(o) { 45216 var t = typeof o; 45217 return _allowable[t] || // number, string, boolean, undefined 45218 _allowable[_toStr.call(o)] || // Number, String, Boolean, Date 45219 (t === OBJECT ? 45220 (o ? OBJECT : NULL) : // object, array, null, misc natives 45221 UNDEFINED); // function, unknown 45222} 45223 45224// Enclose escaped strings in quotes 45225function _string(s) { 45226 return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE; 45227} 45228 45229// Adds the provided space to the beginning of every line in the input string 45230function _indent(s,space) { 45231 return s.replace(/^/gm, space); 45232} 45233 45234// JavaScript implementation of stringify (see API declaration of stringify) 45235function _stringify(o,w,space) { 45236 if (o === undefined) { 45237 return undefined; 45238 } 45239 45240 var replacer = isFunction(w) ? w : null, 45241 format = _toStr.call(space).match(/String|Number/) || [], 45242 _date = YAHOO.lang.JSON.dateToString, 45243 stack = [], 45244 tmp,i,len; 45245 45246 if (replacer || !isArray(w)) { 45247 w = undefined; 45248 } 45249 45250 // Ensure whitelist keys are unique (bug 2110391) 45251 if (w) { 45252 tmp = {}; 45253 for (i = 0, len = w.length; i < len; ++i) { 45254 tmp[w[i]] = true; 45255 } 45256 w = tmp; 45257 } 45258 45259 // Per the spec, strings are truncated to 10 characters and numbers 45260 // are converted to that number of spaces (max 10) 45261 space = format[0] === 'Number' ? 45262 new Array(Math.min(Math.max(0,space),10)+1).join(" ") : 45263 (space || EMPTY).slice(0,10); 45264 45265 function _serialize(h,key) { 45266 var value = h[key], 45267 t = _type(value), 45268 a = [], 45269 colon = space ? COLON_SP : COLON, 45270 arr, i, keys, k, v; 45271 45272 // Per the ECMA 5 spec, toJSON is applied before the replacer is 45273 // called. Also per the spec, Date.prototype.toJSON has been added, so 45274 // Date instances should be serialized prior to exposure to the 45275 // replacer. I disagree with this decision, but the spec is the spec. 45276 if (isObject(value) && isFunction(value.toJSON)) { 45277 value = value.toJSON(key); 45278 } else if (t === DATE) { 45279 value = _date(value); 45280 } 45281 45282 if (isFunction(replacer)) { 45283 value = replacer.call(h,key,value); 45284 } 45285 45286 if (value !== h[key]) { 45287 t = _type(value); 45288 } 45289 45290 switch (t) { 45291 case DATE : // intentional fallthrough. Pre-replacer Dates are 45292 // serialized in the toJSON stage. Dates here would 45293 // have been produced by the replacer. 45294 case OBJECT : break; 45295 case STRING : return _string(value); 45296 case NUMBER : return isFinite(value) ? value+EMPTY : NULL; 45297 case BOOLEAN : return value+EMPTY; 45298 case NULL : return NULL; 45299 default : return undefined; 45300 } 45301 45302 // Check for cyclical references in nested objects 45303 for (i = stack.length - 1; i >= 0; --i) { 45304 if (stack[i] === value) { 45305 throw new Error("JSON.stringify. Cyclical reference"); 45306 } 45307 } 45308 45309 arr = isArray(value); 45310 45311 // Add the object to the processing stack 45312 stack.push(value); 45313 45314 if (arr) { // Array 45315 for (i = value.length - 1; i >= 0; --i) { 45316 a[i] = _serialize(value, i) || NULL; 45317 } 45318 } else { // Object 45319 // If whitelist provided, take only those keys 45320 keys = w || value; 45321 i = 0; 45322 45323 for (k in keys) { 45324 if (l.hasOwnProperty(keys, k)) { 45325 v = _serialize(value, k); 45326 if (v) { 45327 a[i++] = _string(k) + colon + v; 45328 } 45329 } 45330 } 45331 } 45332 45333 // remove the array from the stack 45334 stack.pop(); 45335 45336 if (space && a.length) { 45337 return arr ? 45338 OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A : 45339 OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O; 45340 } else { 45341 return arr ? 45342 OPEN_A + a.join(COMMA) + CLOSE_A : 45343 OPEN_O + a.join(COMMA) + CLOSE_O; 45344 } 45345 } 45346 45347 // process the input 45348 return _serialize({'':o},''); 45349} 45350 45351 45352/* Public API */ 45353YAHOO.lang.JSON = { 45354 /** 45355 * Leverage native JSON parse if the browser has a native implementation. 45356 * In general, this is a good idea. See the Known Issues section in the 45357 * JSON user guide for caveats. The default value is true for browsers with 45358 * native JSON support. 45359 * 45360 * @property useNativeParse 45361 * @type Boolean 45362 * @default true 45363 * @static 45364 */ 45365 useNativeParse : !!Native, 45366 45367 /** 45368 * Leverage native JSON stringify if the browser has a native 45369 * implementation. In general, this is a good idea. See the Known Issues 45370 * section in the JSON user guide for caveats. The default value is true 45371 * for browsers with native JSON support. 45372 * 45373 * @property useNativeStringify 45374 * @type Boolean 45375 * @default true 45376 * @static 45377 */ 45378 useNativeStringify : !!Native, 45379 45380 /** 45381 * Four step determination whether a string is safe to eval. In three steps, 45382 * escape sequences, safe values, and properly placed open square brackets 45383 * are replaced with placeholders or removed. Then in the final step, the 45384 * result of all these replacements is checked for invalid characters. 45385 * 45386 * @method isSafe 45387 * @param str {String} JSON string to be tested 45388 * @return {boolean} is the string safe for eval? 45389 * @static 45390 */ 45391 isSafe : function (s) { 45392 return _isSafe(_prepare(s)); 45393 }, 45394 45395 /** 45396 * <p>Parse a JSON string, returning the native JavaScript 45397 * representation.</p> 45398 * 45399 * <p>When lang.JSON.useNativeParse is true, this will defer to the native 45400 * JSON.parse if the browser has a native implementation. Otherwise, a 45401 * JavaScript implementation based on http://www.json.org/json2.js 45402 * is used.</p> 45403 * 45404 * @method parse 45405 * @param s {string} JSON string data 45406 * @param reviver {function} (optional) function(k,v) passed each key:value 45407 * pair of object literals, allowing pruning or altering values 45408 * @return {MIXED} the native JavaScript representation of the JSON string 45409 * @throws SyntaxError 45410 * @static 45411 */ 45412 parse : function (s,reviver) { 45413 if (typeof s !== 'string') { 45414 s += ''; 45415 } 45416 45417 return Native && YAHOO.lang.JSON.useNativeParse ? 45418 Native.parse(s,reviver) : _parse(s,reviver); 45419 }, 45420 45421 /** 45422 * <p>Converts an arbitrary value to a JSON string representation.</p> 45423 * 45424 * <p>Objects with cyclical references will trigger an exception.</p> 45425 * 45426 * <p>If a whitelist is provided, only matching object keys will be 45427 * included. Alternately, a replacer function may be passed as the 45428 * second parameter. This function is executed on every value in the 45429 * input, and its return value will be used in place of the original value. 45430 * This is useful to serialize specialized objects or class instances.</p> 45431 * 45432 * <p>If a positive integer or non-empty string is passed as the third 45433 * parameter, the output will be formatted with carriage returns and 45434 * indentation for readability. If a String is passed (such as "\t") it
45435 * will be used once for each indentation level. If a number is passed, 45436 * that number of spaces will be used.</p> 45437 * 45438 * <p>When lang.JSON.useNativeStringify is true, this will defer to the 45439 * native JSON.stringify if the browser has a native implementation. 45440 * Otherwise, a JavaScript implementation is used.</p> 45441 * 45442 * @method stringify 45443 * @param o {MIXED} any arbitrary object to convert to JSON string 45444 * @param w {Array|Function} (optional) whitelist of acceptable object keys 45445 * to include OR a function(value,key) to alter values 45446 * before serialization 45447 * @param space {Number|String} (optional) indentation character(s) or 45448 * depthy of spaces to format the output 45449 * @return {string} JSON string representation of the input 45450 * @throws Error 45451 * @static 45452 */ 45453 stringify : function (o,w,space) { 45454 return Native && YAHOO.lang.JSON.useNativeStringify ? 45455 Native.stringify(o,w,space) : _stringify(o,w,space); 45456 }, 45457 45458 /** 45459 * Serializes a Date instance as a UTC date string. Used internally by 45460 * the JavaScript implementation of stringify. If you need a different 45461 * Date serialization format, override this method. If you change this, 45462 * you should also set useNativeStringify to false, since native JSON 45463 * implementations serialize Dates per the ECMAScript 5 spec. You've been 45464 * warned. 45465 * 45466 * @method dateToString 45467 * @param d {Date} The Date to serialize 45468 * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ 45469 * @static 45470 */ 45471 dateToString : function (d) { 45472 function _zeroPad(v) { 45473 return v < 10 ? '0' + v : v; 45474 } 45475 45476 return d.getUTCFullYear() + '-' + 45477 _zeroPad(d.getUTCMonth() + 1) + '-' + 45478 _zeroPad(d.getUTCDate()) + 'T' + 45479 _zeroPad(d.getUTCHours()) + COLON + 45480 _zeroPad(d.getUTCMinutes()) + COLON + 45481 _zeroPad(d.getUTCSeconds()) + 'Z'; 45482 }, 45483 45484 /** 45485 * Reconstitute Date instances from the default JSON UTC serialization. 45486 * Reference this from a reviver function to rebuild Dates during the 45487 * parse operation. 45488 * 45489 * @method stringToDate 45490 * @param str {String} String serialization of a Date 45491 * @return {Date} 45492 */ 45493 stringToDate : function (str) { 45494 var m = str.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/); 45495 if (m) { 45496 var d = new Date(); 45497 d.setUTCFullYear(m[1], m[2]-1, m[3]); 45498 d.setUTCHours(m[4], m[5], m[6], (m[7] || 0)); 45499 return d; 45500 } 45501 return str; 45502 } 45503}; 45504 45505/** 45506 * <p>Four step determination whether a string is safe to eval. In three steps, 45507 * escape sequences, safe values, and properly placed open square brackets 45508 * are replaced with placeholders or removed. Then in the final step, the 45509 * result of all these replacements is checked for invalid characters.</p> 45510 * 45511 * <p>This is an alias for isSafe.</p> 45512 * 45513 * @method isValid 45514 * @param str {String} JSON string to be tested 45515 * @return {boolean} is the string safe for eval? 45516 * @static 45517 * @deprecated use isSafe 45518 */ 45519YAHOO.lang.JSON.isValid = YAHOO.lang.JSON.isSafe; 45520 45521})(); 45522YAHOO.register("json", YAHOO.lang.JSON, {version: "2.9.0", build: "2800"}); 45523/* 45524Copyright (c) 2011, Yahoo! Inc. All rights reserved. 45525Code licensed under the BSD License: 45526http://developer.yahoo.com/yui/license.html 45527version: 2.9.0 45528*/ 45529var Y = YAHOO, 45530 Y_DOM = YAHOO.util.Dom, 45531 EMPTY_ARRAY = [], 45532 Y_UA = Y.env.ua, 45533 Y_Lang = Y.lang, 45534 Y_DOC = document, 45535 Y_DOCUMENT_ELEMENT = Y_DOC.documentElement, 45536 45537 Y_DOM_inDoc = Y_DOM.inDocument, 45538 Y_mix = Y_Lang.augmentObject, 45539 Y_guid = Y_DOM.generateId, 45540 45541 Y_getDoc = function(element) { 45542 var doc = Y_DOC; 45543 if (element) { 45544 doc = (element.nodeType === 9) ? element : // element === document 45545 element.ownerDocument || // element === DOM node 45546 element.document || // element === window 45547 Y_DOC; // default 45548 } 45549 45550 return doc; 45551 }, 45552 45553 Y_Array = function(o, startIdx) { 45554 var l, a, start = startIdx || 0; 45555 45556 // IE errors when trying to slice HTMLElement collections 45557 try { 45558 return Array.prototype.slice.call(o, start); 45559 } catch (e) { 45560 a = []; 45561 l = o.length; 45562 for (; start < l; start++) { 45563 a.push(o[start]); 45564 } 45565 return a; 45566 } 45567 }, 45568 45569 Y_DOM_allById = function(id, root) { 45570 root = root || Y_DOC; 45571 var nodes = [], 45572 ret = [], 45573 i, 45574 node; 45575 45576 if (root.querySelectorAll) { 45577 ret = root.querySelectorAll('[id="' + id + '"]'); 45578 } else if (root.all) { 45579 nodes = root.all(id); 45580 45581 if (nodes) { 45582 // root.all may return HTMLElement or HTMLCollection. 45583 // some elements are also HTMLCollection (FORM, SELECT). 45584 if (nodes.nodeName) { 45585 if (nodes.id === id) { // avoid false positive on name
45586 ret.push(nodes); 45587 nodes = EMPTY_ARRAY; // done, no need to filter 45588 } else { // prep for filtering 45589 nodes = [nodes]; 45590 } 45591 } 45592 45593 if (nodes.length) { 45594 // filter out matches on node.name 45595 // and element.id as reference to element with id === 'id' 45596 for (i = 0; node = nodes[i++];) { 45597 if (node.id === id || 45598 (node.attributes && node.attributes.id && 45599 node.attributes.id.value === id)) { 45600 ret.push(node); 45601 } 45602 } 45603 } 45604 } 45605 } else { 45606 ret = [Y_getDoc(root).getElementById(id)]; 45607 } 45608 45609 return ret; 45610 }; 45611 45612/** 45613 * The selector-native module provides support for native querySelector 45614 * @module dom 45615 * @submodule selector-native 45616 * @for Selector 45617 */ 45618 45619/** 45620 * Provides support for using CSS selectors to query the DOM 45621 * @class Selector 45622 * @static 45623 * @for Selector 45624 */ 45625 45626var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', 45627 OWNER_DOCUMENT = 'ownerDocument', 45628 45629Selector = { 45630 _foundCache: [], 45631 45632 useNative: true, 45633 45634 _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ? 45635 function(nodeA, nodeB) { 45636 var a = nodeA.sourceIndex, 45637 b = nodeB.sourceIndex; 45638 45639 if (a === b) { 45640 return 0; 45641 } else if (a > b) { 45642 return 1; 45643 } 45644 45645 return -1; 45646 45647 } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ? 45648 function(nodeA, nodeB) { 45649 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { 45650 return -1; 45651 } else { 45652 return 1; 45653 } 45654 } : 45655 function(nodeA, nodeB) { 45656 var rangeA, rangeB, compare; 45657 if (nodeA && nodeB) { 45658 rangeA = nodeA[OWNER_DOCUMENT].createRange(); 45659 rangeA.setStart(nodeA, 0); 45660 rangeB = nodeB[OWNER_DOCUMENT].createRange(); 45661 rangeB.setStart(nodeB, 0); 45662 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END 45663 } 45664 45665 return compare; 45666 45667 }), 45668 45669 _sort: function(nodes) { 45670 if (nodes) { 45671 nodes = Y_Array(nodes, 0, true); 45672 if (nodes.sort) { 45673 nodes.sort(Selector._compare); 45674 } 45675 } 45676 45677 return nodes; 45678 }, 45679 45680 _deDupe: function(nodes) { 45681 var ret = [], 45682 i, node; 45683 45684 for (i = 0; (node = nodes[i++]);) { 45685 if (!node._found) { 45686 ret[ret.length] = node; 45687 node._found = true; 45688 } 45689 } 45690 45691 for (i = 0; (node = ret[i++]);) { 45692 node._found = null; 45693 node.removeAttribute('_found'); 45694 } 45695 45696 return ret; 45697 }, 45698 45699 /** 45700 * Retrieves a set of nodes based on a given CSS selector. 45701 * @method query 45702 * 45703 * @param {string} selector The CSS Selector to test the node against. 45704 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc 45705 * @param {Boolean} firstOnly optional Whether or not to return only the first match. 45706 * @return {Array} An array of nodes that match the given selector. 45707 * @static 45708 */ 45709 query: function(selector, root, firstOnly, skipNative) { 45710 if (root && typeof root == 'string') { 45711 root = Y_DOM.get(root); 45712 if (!root) { 45713 return (firstOnly) ? null : []; 45714 } 45715 } else { 45716 root = root || Y_DOC; 45717 } 45718 45719 var ret = [], 45720 useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative), 45721 queries = [[selector, root]], 45722 query, 45723 result, 45724 i, 45725 fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery; 45726 45727 if (selector && fn) { 45728 // split group into seperate queries 45729 if (!skipNative && // already done if skipping 45730 (!useNative || root.tagName)) { // split native when element scoping is needed 45731 queries = Selector._splitQueries(selector, root); 45732 } 45733 45734 for (i = 0; (query = queries[i++]);) {
45735 result = fn(query[0], query[1], firstOnly); 45736 if (!firstOnly) { // coerce DOM Collection to Array 45737 result = Y_Array(result, 0, true); 45738 } 45739 if (result) { 45740 ret = ret.concat(result); 45741 } 45742 } 45743 45744 if (queries.length > 1) { // remove dupes and sort by doc order 45745 ret = Selector._sort(Selector._deDupe(ret)); 45746 } 45747 } 45748 45749 YAHOO.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector'); 45750 return (firstOnly) ? (ret[0] || null) : ret; 45751 45752 }, 45753 45754 // allows element scoped queries to begin with combinator 45755 // e.g. query('> p', document.body) === query('body > p') 45756 _splitQueries: function(selector, node) { 45757 var groups = selector.split(','), 45758 queries = [], 45759 prefix = '', 45760 i, len; 45761 45762 if (node) { 45763 // enforce for element scoping 45764 if (node.tagName) { 45765 node.id = node.id || Y_guid(); 45766 prefix = '[id="' + node.id + '"] '; 45767 } 45768 45769 for (i = 0, len = groups.length; i < len; ++i) { 45770 selector = prefix + groups[i]; 45771 queries.push([selector, node]); 45772 } 45773 } 45774 45775 return queries; 45776 }, 45777 45778 _nativeQuery: function(selector, root, one) { 45779 if (Y_UA.webkit && selector.indexOf(':checked') > -1 && 45780 (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected" 45781 return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query 45782 } 45783 try { 45784 //YAHOO.log('trying native query with: ' + selector, 'info', 'selector-native'); 45785 return root['querySelector' + (one ? '' : 'All')](selector); 45786 } catch(e) { // fallback to brute if available 45787 //YAHOO.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native'); 45788 return Selector.query(selector, root, one, true); // redo with skipNative true 45789 } 45790 }, 45791 45792 filter: function(nodes, selector) { 45793 var ret = [], 45794 i, node; 45795 45796 if (nodes && selector) { 45797 for (i = 0; (node = nodes[i++]);) { 45798 if (Selector.test(node, selector)) { 45799 ret[ret.length] = node; 45800 } 45801 } 45802 } else { 45803 YAHOO.log('invalid filter input (nodes: ' + nodes + 45804 ', selector: ' + selector + ')', 'warn', 'Selector'); 45805 } 45806 45807 return ret; 45808 }, 45809 45810 test: function(node, selector, root) { 45811 var ret = false, 45812 groups = selector.split(','), 45813 useFrag = false, 45814 parent, 45815 item, 45816 items, 45817 frag, 45818 i, j, group; 45819 45820 if (node && node.tagName) { // only test HTMLElements 45821 45822 // we need a root if off-doc 45823 if (!root && !Y_DOM_inDoc(node)) { 45824 parent = node.parentNode; 45825 if (parent) { 45826 root = parent; 45827 } else { // only use frag when no parent to query 45828 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 45829 frag.appendChild(node); 45830 root = frag; 45831 useFrag = true; 45832 } 45833 } 45834 root = root || node[OWNER_DOCUMENT]; 45835 45836 if (!node.id) { 45837 node.id = Y_guid(); 45838 } 45839 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 45840 group += '[id="' + node.id + '"]'; 45841 items = Selector.query(group, root); 45842 45843 for (j = 0; item = items[j++];) { 45844 if (item === node) { 45845 ret = true; 45846 break; 45847 } 45848 } 45849 if (ret) { 45850 break; 45851 } 45852 } 45853 45854 if (useFrag) { // cleanup 45855 frag.removeChild(node); 45856 } 45857 } 45858 45859 return ret; 45860 } 45861 45862}; 45863 45864YAHOO.util.Selector = Selector; 45865/** 45866 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. 45867 * @module dom 45868 * @submodule selector-css2 45869 * @for Selector 45870 */ 45871 45872/** 45873 * Provides helper methods for collecting and filtering DOM elements. 45874 */ 45875 45876var PARENT_NODE = 'parentNode', 45877 TAG_NAME = 'tagName', 45878 ATTRIBUTES = 'attributes', 45879 COMBINATOR = 'combinator', 45880 PSEUDOS = 'pseudos', 45881 45882 SelectorCSS2 = { 45883 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move? 45884 SORT_RESULTS: true, 45885 _children: function(node, tag) { 45886 var ret = node.children, 45887 i, 45888 children = [], 45889 childNodes, 45890 child; 45891 45892 if (node.children && tag && node.children.tags) { 45893 children = node.children.tags(tag); 45894 } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children 45895 childNodes = ret || node.childNodes; 45896 ret = []; 45897 for (i = 0; (child = childNodes[i++]);) { 45898 if (child.tagName) { 45899 if (!tag || tag === child.tagName) {
45900 ret.push(child); 45901 } 45902 } 45903 } 45904 } 45905 45906 return ret || []; 45907 }, 45908 45909 _re: { 45910 //attr: /(\[.*\])/g, 45911 attr: /(\[[^\]]*\])/g, 45912 //esc: /\\[:\[][\w\d\]]*/gi, 45913 esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi, 45914 //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i 45915 pseudos: /(\([^\)]*\))/g 45916 }, 45917 45918 /** 45919 * Mapping of shorthand tokens to corresponding attribute selector 45920 * @property shorthand 45921 * @type object 45922 */ 45923 shorthand: { 45924 //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]', 45925 '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]', 45926 //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]', 45927 //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]' 45928 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' 45929 }, 45930 45931 /** 45932 * List of operators and corresponding boolean functions. 45933 * These functions are passed the attribute and the current node's value of the attribute. 45934 * @property operators 45935 * @type object 45936 */ 45937 operators: { 45938 '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute 45939 //'': '.+', 45940 //'=': '^{val}$', // equality 45941 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited 45942 '|=': '^{val}(?:-|$)' // optional hyphen-delimited 45943 }, 45944 45945 pseudos: { 45946 'first-child': function(node) { 45947 return Selector._children(node[PARENT_NODE])[0] === node; 45948 } 45949 }, 45950 45951 _bruteQuery: function(selector, root, firstOnly) { 45952 var ret = [], 45953 nodes = [], 45954 tokens = Selector._tokenize(selector), 45955 token = tokens[tokens.length - 1], 45956 rootDoc = Y_getDoc(root), 45957 child, 45958 id, 45959 className, 45960 tagName; 45961 45962 45963 // if we have an initial ID, set to root when in document 45964 /* 45965 if (tokens[0] && rootDoc === root && 45966 (id = tokens[0].id) && 45967 rootDoc.getElementById(id)) { 45968 root = rootDoc.getElementById(id); 45969 } 45970 */ 45971 45972 if (token) { 45973 // prefilter nodes 45974 id = token.id; 45975 className = token.className; 45976 tagName = token.tagName || '*'; 45977 45978 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags 45979 // try ID first, unless no root.all && root not in document 45980 // (root.all works off document, but not getElementById) 45981 // TODO: move to allById? 45982 if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) { 45983 nodes = Y_DOM_allById(id, root); 45984 // try className 45985 } else if (className) { 45986 nodes = root.getElementsByClassName(className); 45987 } else { // default to tagName 45988 nodes = root.getElementsByTagName(tagName); 45989 } 45990 45991 } else { // brute getElementsByTagName('*') 45992 child = root.firstChild; 45993 while (child) { 45994 if (child.tagName) { // only collect HTMLElements 45995 nodes.push(child); 45996 } 45997 child = child.nextSilbing || child.firstChild; 45998 } 45999 } 46000 if (nodes.length) { 46001 ret = Selector._filterNodes(nodes, tokens, firstOnly); 46002 } 46003 } 46004 46005 return ret; 46006 }, 46007 46008 _filterNodes: function(nodes, tokens, firstOnly) { 46009 var i = 0, 46010 j, 46011 len = tokens.length, 46012 n = len - 1, 46013 result = [], 46014 node = nodes[0], 46015 tmpNode = node, 46016 getters = Selector.getters, 46017 operator, 46018 combinator, 46019 token, 46020 path, 46021 pass, 46022 //FUNCTION = 'function', 46023 value, 46024 tests, 46025 test; 46026 46027 //do { 46028 for (i = 0; (tmpNode = node = nodes[i++]);) { 46029 n = len - 1; 46030 path = null; 46031 46032 testLoop: 46033 while (tmpNode && tmpNode.tagName) { 46034 token = tokens[n]; 46035 tests = token.tests; 46036 j = tests.length; 46037 if (j && !pass) { 46038 while ((test = tests[--j])) { 46039 operator = test[1]; 46040 if (getters[test[0]]) { 46041 value = getters[test[0]](tmpNode, test[0]); 46042 } else { 46043 value = tmpNode[test[0]]; 46044 // use getAttribute for non-standard attributes 46045 if (value === undefined && tmpNode.getAttribute) { 46046 value = tmpNode.getAttribute(test[0]); 46047 } 46048 } 46049 46050 if ((operator === '=' && value !== test[2]) || // fast path for equality 46051 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) 46052 operator.test && !operator.test(value)) || // regex test 46053 (!operator.test && // protect against RegExp as function (webkit) 46054 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test 46055 46056 // skip non element nodes or non-matching tags 46057 if ((tmpNode = tmpNode[path])) { 46058 while (tmpNode && 46059 (!tmpNode.tagName || 46060 (token.tagName && token.tagName !== tmpNode.tagName)) 46061 ) { 46062 tmpNode = tmpNode[path]; 46063 } 46064 } 46065 continue testLoop; 46066 } 46067 } 46068 } 46069 46070 n--; // move to next token 46071 // now that we've passed the test, move up the tree by combinator 46072 if (!pass && (combinator = token.combinator)) { 46073 path = combinator.axis; 46074 tmpNode = tmpNode[path]; 46075 46076 // skip non element nodes 46077 while (tmpNode && !tmpNode.tagName) { 46078 tmpNode = tmpNode[path]; 46079 } 46080 46081 if (combinator.direct) { // one pass only 46082 path = null; 46083 } 46084 46085 } else { // success if we made it this far 46086 result.push(node); 46087 if (firstOnly) { 46088 return result; 46089 } 46090 break; 46091 } 46092 } 46093 }// while (tmpNode = node = nodes[++i]); 46094 node = tmpNode = null; 46095 return result; 46096 }, 46097 46098 combinators: { 46099 ' ': { 46100 axis: 'parentNode' 46101 }, 46102 46103 '>': { 46104 axis: 'parentNode', 46105 direct: true 46106 }, 46107 46108 46109 '+': { 46110 axis: 'previousSibling', 46111 direct: true 46112 } 46113 }, 46114 46115 _parsers: [ 46116 { 46117 name: ATTRIBUTES, 46118 //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, 46119 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, 46120 fn: function(match, token) { 46121 var operator = match[2] || '', 46122 operators = Selector.operators, 46123 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', 46124 test; 46125 46126 // add prefiltering for ID and CLASS 46127 if ((match[1] === 'id' && operator === '=') || 46128 (match[1] === 'className' && 46129 Y_DOCUMENT_ELEMENT.getElementsByClassName && 46130 (operator === '~=' || operator === '='))) { 46131 token.prefilter = match[1]; 46132 46133 46134 match[3] = escVal; 46135 46136 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) 46137 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; 46138 46139 } 46140 46141 // add tests 46142 if (operator in operators) { 46143 test = operators[operator]; 46144 if (typeof test === 'string') { 46145 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); 46146 test = new RegExp(test.replace('{val}', match[3])); 46147 } 46148 match[2] = test; 46149 } 46150 if (!token.last || token.prefilter !== match[1]) { 46151 return match.slice(1); 46152 } 46153 } 46154 46155 }, 46156 { 46157 name: TAG_NAME, 46158 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, 46159 fn: function(match, token) { 46160 var tag = match[1].toUpperCase(); 46161 token.tagName = tag; 46162 46163 if (tag !== '*' && (!token.last || token.prefilter)) { 46164 return [TAG_NAME, '=', tag]; 46165 } 46166 if (!token.prefilter) { 46167 token.prefilter = 'tagName'; 46168 } 46169 } 46170 }, 46171 { 46172 name: COMBINATOR, 46173 re: /^\s*([>+~]|\s)\s*/, 46174 fn: function(match, token) { 46175 } 46176 }, 46177 { 46178 name: PSEUDOS, 46179 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, 46180 fn: function(match, token) { 46181 var test = Selector[PSEUDOS][match[1]]; 46182 if (test) { // reorder match array and unescape special chars for tests 46183 if (match[2]) { 46184 match[2] = match[2].replace(/\\/g, ''); 46185 } 46186 return [match[2], test]; 46187 } else { // selector token not supported (possibly missing CSS3 module) 46188 return false;
46189 } 46190 } 46191 } 46192 ], 46193 46194 _getToken: function(token) { 46195 return { 46196 tagName: null, 46197 id: null, 46198 className: null, 46199 attributes: {}, 46200 combinator: null, 46201 tests: [] 46202 }; 46203 }, 46204 46205 /** 46206 Break selector into token units per simple selector. 46207 Combinator is attached to the previous token. 46208 */ 46209 _tokenize: function(selector) { 46210 selector = selector || ''; 46211 selector = Selector._replaceShorthand(Y_Lang.trim(selector)); 46212 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) 46213 query = selector, // original query for debug report 46214 tokens = [], // array of tokens 46215 found = false, // whether or not any matches were found this pass 46216 match, // the regex match 46217 test, 46218 i, parser; 46219 46220 /* 46221 Search for selector patterns, store, and strip them from the selector string 46222 until no patterns match (invalid selector) or we run out of chars. 46223 46224 Multiple attributes and pseudos are allowed, in any order. 46225 for example:
46226 'form:first-child[type=button]:not(button)[lang|=en]' 46227 */ 46228 46229 outer: 46230 do { 46231 found = false; // reset after full pass 46232 46233 for (i = 0; (parser = Selector._parsers[i++]);) { 46234 if ( (match = parser.re.exec(selector)) ) { // note assignment 46235 if (parser.name !== COMBINATOR ) { 46236 token.selector = selector; 46237 } 46238 selector = selector.replace(match[0], ''); // strip current match from selector 46239 if (!selector.length) { 46240 token.last = true; 46241 } 46242 46243 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. 46244 match[1] = Selector._attrFilters[match[1]]; 46245 } 46246 46247 test = parser.fn(match, token); 46248 if (test === false) { // selector not supported 46249 found = false; 46250 break outer; 46251 } else if (test) { 46252 token.tests.push(test); 46253 } 46254 46255 if (!selector.length || parser.name === COMBINATOR) { 46256 tokens.push(token); 46257 token = Selector._getToken(token); 46258 if (parser.name === COMBINATOR) { 46259 token.combinator = Selector.combinators[match[1]]; 46260 } 46261 } 46262 found = true; 46263 46264 46265 } 46266 } 46267 } while (found && selector.length); 46268 46269 if (!found || selector.length) { // not fully parsed 46270 YAHOO.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector'); 46271 tokens = []; 46272 } 46273 return tokens; 46274 }, 46275 46276 _replaceShorthand: function(selector) { 46277 var shorthand = Selector.shorthand, 46278 esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc. 46279 attrs, 46280 pseudos, 46281 re, i, len; 46282 46283 if (esc) { 46284 selector = selector.replace(Selector._re.esc, '\uE000'); 46285 } 46286 46287 attrs = selector.match(Selector._re.attr); 46288 pseudos = selector.match(Selector._re.pseudos); 46289 46290 if (attrs) { 46291 selector = selector.replace(Selector._re.attr, '\uE001'); 46292 } 46293 46294 if (pseudos) { 46295 selector = selector.replace(Selector._re.pseudos, '\uE002'); 46296 } 46297 46298 46299 for (re in shorthand) { 46300 if (shorthand.hasOwnProperty(re)) { 46301 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); 46302 } 46303 } 46304 46305 if (attrs) { 46306 for (i = 0, len = attrs.length; i < len; ++i) { 46307 selector = selector.replace(/\uE001/, attrs[i]); 46308 } 46309 } 46310 46311 if (pseudos) { 46312 for (i = 0, len = pseudos.length; i < len; ++i) { 46313 selector = selector.replace(/\uE002/, pseudos[i]); 46314 } 46315 } 46316 46317 selector = selector.replace(/\[/g, '\uE003'); 46318 selector = selector.replace(/\]/g, '\uE004'); 46319 46320 selector = selector.replace(/\(/g, '\uE005'); 46321 selector = selector.replace(/\)/g, '\uE006'); 46322 46323 if (esc) { 46324 for (i = 0, len = esc.length; i < len; ++i) { 46325 selector = selector.replace('\uE000', esc[i]); 46326 } 46327 } 46328 46329 return selector; 46330 }, 46331 46332 _attrFilters: { 46333 'class': 'className', 46334 'for': 'htmlFor' 46335 }, 46336 46337 getters: { 46338 href: function(node, attr) { 46339 return Y_DOM.getAttribute(node, attr); 46340 } 46341 } 46342 }; 46343 46344Y_mix(Selector, SelectorCSS2, true); 46345Selector.getters.src = Selector.getters.rel = Selector.getters.href; 46346
46347// IE wants class with native queries 46348if (Selector.useNative && Y_DOC.querySelector) { 46349 Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]'; 46350} 46351 46352/** 46353 * The selector css3 module provides support for css3 selectors. 46354 * @module dom 46355 * @submodule selector-css3 46356 * @for Selector 46357 */ 46358 46359/* 46360 an+b = get every _a_th node starting at the _b_th 46361 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element 46362 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") 46363 an+0 = get every _a_th element, "0" may be omitted 46364*/ 46365 46366Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; 46367 46368Selector._getNth = function(node, expr, tag, reverse) { 46369 Selector._reNth.test(expr); 46370 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) 46371 n = RegExp.$2, // "n" 46372 oddeven = RegExp.$3, // "odd" or "even" 46373 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ 46374 result = [], 46375 siblings = Selector._children(node.parentNode, tag), 46376 op; 46377 46378 if (oddeven) { 46379 a = 2; // always every other 46380 op = '+'; 46381 n = 'n'; 46382 b = (oddeven === 'odd') ? 1 : 0; 46383 } else if ( isNaN(a) ) { 46384 a = (n) ? 1 : 0; // start from the first or no repeat 46385 } 46386 46387 if (a === 0) { // just the first 46388 if (reverse) { 46389 b = siblings.length - b + 1; 46390 } 46391 46392 if (siblings[b - 1] === node) { 46393 return true; 46394 } else { 46395 return false; 46396 } 46397 46398 } else if (a < 0) { 46399 reverse = !!reverse; 46400 a = Math.abs(a); 46401 } 46402 46403 if (!reverse) { 46404 for (var i = b - 1, len = siblings.length; i < len; i += a) { 46405 if ( i >= 0 && siblings[i] === node ) { 46406 return true; 46407 } 46408 } 46409 } else { 46410 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { 46411 if ( i < len && siblings[i] === node ) { 46412 return true; 46413 } 46414 } 46415 } 46416 return false; 46417}; 46418 46419Y_mix(Selector.pseudos, { 46420 'root': function(node) { 46421 return node === node.ownerDocument.documentElement; 46422 }, 46423 46424 'nth-child': function(node, expr) { 46425 return Selector._getNth(node, expr); 46426 }, 46427 46428 'nth-last-child': function(node, expr) { 46429 return Selector._getNth(node, expr, null, true); 46430 }, 46431 46432 'nth-of-type': function(node, expr) { 46433 return Selector._getNth(node, expr, node.tagName); 46434 }, 46435 46436 'nth-last-of-type': function(node, expr) { 46437 return Selector._getNth(node, expr, node.tagName, true); 46438 }, 46439 46440 'last-child': function(node) { 46441 var children = Selector._children(node.parentNode); 46442 return children[children.length - 1] === node; 46443 }, 46444 46445 'first-of-type': function(node) { 46446 return Selector._children(node.parentNode, node.tagName)[0] === node; 46447 }, 46448 46449 'last-of-type': function(node) { 46450 var children = Selector._children(node.parentNode, node.tagName); 46451 return children[children.length - 1] === node; 46452 }, 46453 46454 'only-child': function(node) { 46455 var children = Selector._children(node.parentNode); 46456 return children.length === 1 && children[0] === node; 46457 }, 46458 46459 'only-of-type': function(node) { 46460 var children = Selector._children(node.parentNode, node.tagName); 46461 return children.length === 1 && children[0] === node; 46462 }, 46463 46464 'empty': function(node) { 46465 return node.childNodes.length === 0; 46466 }, 46467 46468 'not': function(node, expr) { 46469 return !Selector.test(node, expr); 46470 }, 46471 46472 'contains': function(node, expr) { 46473 var text = node.innerText || node.textContent || ''; 46474 return text.indexOf(expr) > -1; 46475 }, 46476 46477 'checked': function(node) { 46478 return (node.checked === true || node.selected === true); 46479 }, 46480 46481 enabled: function(node) { 46482 return (node.disabled !== undefined && !node.disabled); 46483 }, 46484 46485 disabled: function(node) { 46486 return (node.disabled); 46487 } 46488}); 46489 46490Y_mix(Selector.operators, { 46491 '^=': '^{val}', // Match starts with value 46492 '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value 46493 '$=': '{val}$', // Match ends with value 46494 '*=': '{val}' // Match contains value as substring 46495}); 46496 46497Selector.combinators['~'] = { 46498 axis: 'previousSibling' 46499}; 46500YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"}); 46501/* 46502Copyright (c) 2011, Yahoo! Inc. All rights reserved. 46503Code licensed under the BSD License:
46504http://developer.yahoo.com/yui/license.html 46505version: 2.9.0 46506*/ 46507(function () { 46508 46509var lang = YAHOO.lang, 46510 util = YAHOO.util, 46511 Ev = util.Event; 46512 46513/** 46514 * The DataSource utility provides a common configurable interface for widgets to 46515 * access a variety of data, from JavaScript arrays to online database servers. 46516 * 46517 * @module datasource 46518 * @requires yahoo, event 46519 * @optional json, get, connection 46520 * @title DataSource Utility 46521 */ 46522 46523/****************************************************************************/ 46524/****************************************************************************/ 46525/****************************************************************************/ 46526 46527/** 46528 * Base class for the YUI DataSource utility. 46529 * 46530 * @namespace YAHOO.util 46531 * @class YAHOO.util.DataSourceBase 46532 * @constructor 46533 * @param oLiveData {HTMLElement} Pointer to live data. 46534 * @param oConfigs {object} (optional) Object literal of configuration values. 46535 */ 46536util.DataSourceBase = function(oLiveData, oConfigs) { 46537 if(oLiveData === null || oLiveData === undefined) { 46538 YAHOO.log("Could not instantiate DataSource due to invalid live database", 46539 "error", this.toString()); 46540 return; 46541 } 46542 46543 this.liveData = oLiveData; 46544 this._oQueue = {interval:null, conn:null, requests:[]}; 46545 this.responseSchema = {}; 46546 46547 // Set any config params passed in to override defaults 46548 if(oConfigs && (oConfigs.constructor == Object)) { 46549 for(var sConfig in oConfigs) { 46550 if(sConfig) { 46551 this[sConfig] = oConfigs[sConfig]; 46552 } 46553 } 46554 } 46555 46556 // Validate and initialize public configs 46557 var maxCacheEntries = this.maxCacheEntries; 46558 if(!lang.isNumber(maxCacheEntries) || (maxCacheEntries < 0)) { 46559 maxCacheEntries = 0; 46560 } 46561 46562 // Initialize interval tracker 46563 this._aIntervals = []; 46564 46565 ///////////////////////////////////////////////////////////////////////////// 46566 // 46567 // Custom Events 46568 // 46569 ///////////////////////////////////////////////////////////////////////////// 46570 46571 /** 46572 * Fired when a request is made to the local cache. 46573 * 46574 * @event cacheRequestEvent 46575 * @param oArgs.request {Object} The request object. 46576 * @param oArgs.callback {Object} The callback object. 46577 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46578 */ 46579 this.createEvent("cacheRequestEvent"); 46580 46581 /** 46582 * Fired when data is retrieved from the local cache. 46583 * 46584 * @event cacheResponseEvent 46585 * @param oArgs.request {Object} The request object. 46586 * @param oArgs.response {Object} The response object. 46587 * @param oArgs.callback {Object} The callback object. 46588 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46589 */ 46590 this.createEvent("cacheResponseEvent"); 46591 46592 /** 46593 * Fired when a request is sent to the live data source. 46594 * 46595 * @event requestEvent 46596 * @param oArgs.request {Object} The request object. 46597 * @param oArgs.callback {Object} The callback object. 46598 * @param oArgs.tId {Number} Transaction ID. 46599 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46600 */ 46601 this.createEvent("requestEvent"); 46602 46603 /** 46604 * Fired when live data source sends response. 46605 * 46606 * @event responseEvent 46607 * @param oArgs.request {Object} The request object. 46608 * @param oArgs.response {Object} The raw response object. 46609 * @param oArgs.callback {Object} The callback object. 46610 * @param oArgs.tId {Number} Transaction ID. 46611 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46612 */ 46613 this.createEvent("responseEvent"); 46614 46615 /** 46616 * Fired when response is parsed. 46617 * 46618 * @event responseParseEvent 46619 * @param oArgs.request {Object} The request object. 46620 * @param oArgs.response {Object} The parsed response object. 46621 * @param oArgs.callback {Object} The callback object. 46622 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46623 */ 46624 this.createEvent("responseParseEvent"); 46625 46626 /** 46627 * Fired when response is cached. 46628 *
46629 * @event responseCacheEvent 46630 * @param oArgs.request {Object} The request object. 46631 * @param oArgs.response {Object} The parsed response object. 46632 * @param oArgs.callback {Object} The callback object. 46633 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46634 */ 46635 this.createEvent("responseCacheEvent"); 46636 /** 46637 * Fired when an error is encountered with the live data source. 46638 * 46639 * @event dataErrorEvent 46640 * @param oArgs.request {Object} The request object. 46641 * @param oArgs.response {String} The response object (if available). 46642 * @param oArgs.callback {Object} The callback object. 46643 * @param oArgs.caller {Object} (deprecated) Use callback.scope. 46644 * @param oArgs.message {String} The error message. 46645 */ 46646 this.createEvent("dataErrorEvent"); 46647 46648 /** 46649 * Fired when the local cache is flushed. 46650 * 46651 * @event cacheFlushEvent 46652 */ 46653 this.createEvent("cacheFlushEvent"); 46654 46655 var DS = util.DataSourceBase; 46656 this._sName = "DataSource instance" + DS._nIndex; 46657 DS._nIndex++; 46658 YAHOO.log("DataSource initialized", "info", this.toString()); 46659}; 46660 46661var DS = util.DataSourceBase; 46662 46663lang.augmentObject(DS, { 46664 46665///////////////////////////////////////////////////////////////////////////// 46666// 46667// DataSourceBase public constants 46668// 46669///////////////////////////////////////////////////////////////////////////// 46670 46671/** 46672 * Type is unknown. 46673 * 46674 * @property TYPE_UNKNOWN 46675 * @type Number 46676 * @final 46677 * @default -1 46678 */ 46679TYPE_UNKNOWN : -1, 46680 46681/** 46682 * Type is a JavaScript Array. 46683 * 46684 * @property TYPE_JSARRAY 46685 * @type Number 46686 * @final 46687 * @default 0 46688 */ 46689TYPE_JSARRAY : 0, 46690 46691/** 46692 * Type is a JavaScript Function. 46693 * 46694 * @property TYPE_JSFUNCTION 46695 * @type Number 46696 * @final 46697 * @default 1 46698 */ 46699TYPE_JSFUNCTION : 1, 46700 46701/** 46702 * Type is hosted on a server via an XHR connection. 46703 * 46704 * @property TYPE_XHR 46705 * @type Number 46706 * @final 46707 * @default 2 46708 */ 46709TYPE_XHR : 2, 46710 46711/** 46712 * Type is JSON. 46713 * 46714 * @property TYPE_JSON 46715 * @type Number 46716 * @final 46717 * @default 3 46718 */ 46719TYPE_JSON : 3, 46720 46721/** 46722 * Type is XML. 46723 * 46724 * @property TYPE_XML 46725 * @type Number 46726 * @final 46727 * @default 4 46728 */ 46729TYPE_XML : 4, 46730 46731/** 46732 * Type is plain text. 46733 * 46734 * @property TYPE_TEXT 46735 * @type Number 46736 * @final 46737 * @default 5 46738 */ 46739TYPE_TEXT : 5, 46740 46741/** 46742 * Type is an HTML TABLE element. Data is parsed out of TR elements from all TBODY elements. 46743 * 46744 * @property TYPE_HTMLTABLE 46745 * @type Number 46746 * @final 46747 * @default 6 46748 */ 46749TYPE_HTMLTABLE : 6, 46750 46751/** 46752 * Type is hosted on a server via a dynamic script node. 46753 * 46754 * @property TYPE_SCRIPTNODE 46755 * @type Number 46756 * @final 46757 * @default 7 46758 */ 46759TYPE_SCRIPTNODE : 7, 46760 46761/** 46762 * Type is local. 46763 * 46764 * @property TYPE_LOCAL 46765 * @type Number 46766 * @final 46767 * @default 8 46768 */ 46769TYPE_LOCAL : 8, 46770 46771/** 46772 * Error message for invalid dataresponses. 46773 * 46774 * @property ERROR_DATAINVALID 46775 * @type String 46776 * @final 46777 * @default "Invalid data" 46778 */ 46779ERROR_DATAINVALID : "Invalid data", 46780 46781/** 46782 * Error message for null data responses. 46783 * 46784 * @property ERROR_DATANULL 46785 * @type String 46786 * @final 46787 * @default "Null data" 46788 */ 46789ERROR_DATANULL : "Null data", 46790 46791///////////////////////////////////////////////////////////////////////////// 46792// 46793// DataSourceBase private static properties 46794// 46795///////////////////////////////////////////////////////////////////////////// 46796 46797/** 46798 * Internal class variable to index multiple DataSource instances. 46799 * 46800 * @property DataSourceBase._nIndex 46801 * @type Number 46802 * @private 46803 * @static 46804 */ 46805_nIndex : 0, 46806 46807/** 46808 * Internal class variable to assign unique transaction IDs. 46809 * 46810 * @property DataSourceBase._nTransactionId 46811 * @type Number 46812 * @private 46813 * @static 46814 */ 46815_nTransactionId : 0, 46816 46817///////////////////////////////////////////////////////////////////////////// 46818// 46819// DataSourceBase private static methods 46820// 46821///////////////////////////////////////////////////////////////////////////// 46822/** 46823 * Clones object literal or array of object literals. 46824 * 46825 * @method DataSourceBase._cloneObject 46826 * @param o {Object} Object. 46827 * @private 46828 * @static 46829 */ 46830_cloneObject: function(o) { 46831 if(!lang.isValue(o)) { 46832 return o; 46833 } 46834 46835 var copy = {}; 46836 46837 if(Object.prototype.toString.apply(o) === "[object RegExp]") { 46838 copy = o; 46839 } 46840 else if(lang.isFunction(o)) { 46841 copy = o; 46842 } 46843 else if(lang.isArray(o)) { 46844 var array = []; 46845 for(var i=0,len=o.length;i<len;i++) { 46846 array[i] = DS._cloneObject(o[i]); 46847 } 46848 copy = array; 46849 } 46850 else if(lang.isObject(o)) { 46851 for (var x in o){ 46852 if(lang.hasOwnProperty(o, x)) { 46853 if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) { 46854 copy[x] = DS._cloneObject(o[x]); 46855 } 46856 else { 46857 copy[x] = o[x]; 46858 } 46859 } 46860 } 46861 } 46862 else { 46863 copy = o; 46864 } 46865 46866 return copy; 46867}, 46868 46869/** 46870 * Get an XPath-specified value for a given field from an XML node or document. 46871 * 46872 * @method _getLocationValue 46873 * @param field {String | Object} Field definition. 46874 * @param context {Object} XML node or document to search within. 46875 * @return {Object} Data value or null. 46876 * @static 46877 * @private 46878 */ 46879_getLocationValue: function(field, context) { 46880 var locator = field.locator || field.key || field, 46881 xmldoc = context.ownerDocument || context, 46882 result, res, value = null; 46883 46884 try { 46885 // Standards mode 46886 if(!lang.isUndefined(xmldoc.evaluate)) { 46887 result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null); 46888 while(res = result.iterateNext()) { 46889 value = res.textContent; 46890 } 46891 } 46892 // IE mode 46893 else { 46894 xmldoc.setProperty("SelectionLanguage", "XPath"); 46895 result = context.selectNodes(locator)[0]; 46896 value = result.value || result.text || null; 46897 } 46898 return value; 46899 46900 } 46901 catch(e) { 46902 } 46903}, 46904 46905///////////////////////////////////////////////////////////////////////////// 46906// 46907// DataSourceBase public static methods 46908// 46909///////////////////////////////////////////////////////////////////////////// 46910 46911/** 46912 * Executes a configured callback. For object literal callbacks, the third 46913 * param determines whether to execute the success handler or failure handler. 46914 * 46915 * @method issueCallback 46916 * @param callback {Function|Object} the callback to execute 46917 * @param params {Array} params to be passed to the callback method 46918 * @param error {Boolean} whether an error occurred 46919 * @param scope {Object} the scope from which to execute the callback 46920 * (deprecated - use an object literal callback) 46921 * @static 46922 */ 46923issueCallback : function (callback,params,error,scope) { 46924 if (lang.isFunction(callback)) { 46925 callback.apply(scope, params); 46926 } else if (lang.isObject(callback)) { 46927 scope = callback.scope || scope || window; 46928 var callbackFunc = callback.success; 46929 if (error) { 46930 callbackFunc = callback.failure; 46931 } 46932 if (callbackFunc) { 46933 callbackFunc.apply(scope, params.concat([callback.argument])); 46934 } 46935 } 46936}, 46937 46938/**
46939 * Converts data to type String. 46940 * 46941 * @method DataSourceBase.parseString 46942 * @param oData {String | Number | Boolean | Date | Array | Object} Data to parse. 46943 * The special values null and undefined will return null. 46944 * @return {String} A string, or null. 46945 * @static 46946 */ 46947parseString : function(oData) { 46948 // Special case null and undefined 46949 if(!lang.isValue(oData)) { 46950 return null; 46951 } 46952 46953 //Convert to string 46954 var string = oData + ""; 46955 46956 // Validate 46957 if(lang.isString(string)) { 46958 return string; 46959 } 46960 else { 46961 YAHOO.log("Could not convert data " + lang.dump(oData) + " to type String", "warn", this.toString()); 46962 return null; 46963 } 46964}, 46965 46966/** 46967 * Converts data to type Number. 46968 * 46969 * @method DataSourceBase.parseNumber 46970 * @param oData {String | Number | Boolean} Data to convert. Note, the following 46971 * values return as null: null, undefined, NaN, "". 46972 * @return {Number} A number, or null. 46973 * @static 46974 */ 46975parseNumber : function(oData) { 46976 if(!lang.isValue(oData) || (oData === "")) { 46977 return null; 46978 } 46979 46980 //Convert to number 46981 var number = oData * 1; 46982 46983 // Validate 46984 if(lang.isNumber(number)) { 46985 return number; 46986 } 46987 else { 46988 YAHOO.log("Could not convert data " + lang.dump(oData) + " to type Number", "warn", this.toString()); 46989 return null; 46990 } 46991}, 46992// Backward compatibility
46993convertNumber : function(oData) { 46994 YAHOO.log("The method YAHOO.util.DataSourceBase.convertNumber() has been" + 46995 " deprecated in favor of YAHOO.util.DataSourceBase.parseNumber()", "warn", 46996 this.toString()); 46997 return DS.parseNumber(oData); 46998}, 46999 47000/** 47001 * Converts data to type Date. 47002 * 47003 * @method DataSourceBase.parseDate 47004 * @param oData {Date | String | Number} Data to convert. 47005 * @return {Date} A Date instance. 47006 * @static 47007 */ 47008parseDate : function(oData) { 47009 var date = null; 47010 47011 //Convert to date 47012 if(lang.isValue(oData) && !(oData instanceof Date)) { 47013 date = new Date(oData); 47014 } 47015 else { 47016 return oData; 47017 } 47018 47019 // Validate 47020 if(date instanceof Date) { 47021 return date; 47022 } 47023 else { 47024 YAHOO.log("Could not convert data " + lang.dump(oData) + " to type Date", "warn", this.toString()); 47025 return null; 47026 } 47027}, 47028// Backward compatibility 47029convertDate : function(oData) { 47030 YAHOO.log("The method YAHOO.util.DataSourceBase.convertDate() has been" + 47031 " deprecated in favor of YAHOO.util.DataSourceBase.parseDate()", "warn", 47032 this.toString()); 47033 return DS.parseDate(oData); 47034} 47035 47036}); 47037 47038// Done in separate step so referenced functions are defined. 47039/** 47040 * Data parsing functions. 47041 * @property DataSource.Parser 47042 * @type Object 47043 * @static 47044 */ 47045DS.Parser = { 47046 string : DS.parseString, 47047 number : DS.parseNumber, 47048 date : DS.parseDate 47049}; 47050 47051// Prototype properties and methods 47052DS.prototype = { 47053 47054///////////////////////////////////////////////////////////////////////////// 47055// 47056// DataSourceBase private properties 47057// 47058///////////////////////////////////////////////////////////////////////////// 47059 47060/** 47061 * Name of DataSource instance. 47062 * 47063 * @property _sName 47064 * @type String 47065 * @private 47066 */ 47067_sName : null, 47068 47069/** 47070 * Local cache of data result object literals indexed chronologically. 47071 * 47072 * @property _aCache 47073 * @type Object[] 47074 * @private 47075 */ 47076_aCache : null, 47077 47078/** 47079 * Local queue of request connections, enabled if queue needs to be managed. 47080 * 47081 * @property _oQueue 47082 * @type Object 47083 * @private 47084 */ 47085_oQueue : null, 47086 47087/** 47088 * Array of polling interval IDs that have been enabled, needed to clear all intervals. 47089 * 47090 * @property _aIntervals 47091 * @type Array 47092 * @private 47093 */ 47094_aIntervals : null, 47095 47096///////////////////////////////////////////////////////////////////////////// 47097// 47098// DataSourceBase public properties 47099// 47100///////////////////////////////////////////////////////////////////////////// 47101 47102/** 47103 * Max size of the local cache. Set to 0 to turn off caching. Caching is 47104 * useful to reduce the number of server connections. Recommended only for data 47105 * sources that return comprehensive results for queries or when stale data is 47106 * not an issue. 47107 * 47108 * @property maxCacheEntries 47109 * @type Number 47110 * @default 0 47111 */ 47112maxCacheEntries : 0, 47113 47114 /** 47115 * Pointer to live database. 47116 * 47117 * @property liveData 47118 * @type Object 47119 */ 47120liveData : null, 47121 47122/** 47123 * Where the live data is held: 47124 * 47125 * <dl> 47126 * <dt>TYPE_UNKNOWN</dt> 47127 * <dt>TYPE_LOCAL</dt> 47128 * <dt>TYPE_XHR</dt> 47129 * <dt>TYPE_SCRIPTNODE</dt> 47130 * <dt>TYPE_JSFUNCTION</dt> 47131 * </dl> 47132 * 47133 * @property dataType 47134 * @type Number 47135 * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN 47136 * 47137 */ 47138dataType : DS.TYPE_UNKNOWN, 47139 47140/** 47141 * Format of response: 47142 * 47143 * <dl> 47144 * <dt>TYPE_UNKNOWN</dt> 47145 * <dt>TYPE_JSARRAY</dt> 47146 * <dt>TYPE_JSON</dt> 47147 * <dt>TYPE_XML</dt> 47148 * <dt>TYPE_TEXT</dt> 47149 * <dt>TYPE_HTMLTABLE</dt> 47150 * </dl> 47151 * 47152 * @property responseType 47153 * @type Number 47154 * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN 47155 */ 47156responseType : DS.TYPE_UNKNOWN, 47157 47158/** 47159 * Response schema object literal takes a combination of the following properties: 47160 * 47161 * <dl> 47162 * <dt>resultsList</dt> <dd>Pointer to array of tabular data</dd> 47163 * <dt>resultNode</dt> <dd>Pointer to node name of row data (XML data only)</dd> 47164 * <dt>recordDelim</dt> <dd>Record delimiter (text data only)</dd> 47165 * <dt>fieldDelim</dt> <dd>Field delimiter (text data only)</dd> 47166 * <dt>fields</dt> <dd>Array of field names (aka keys), or array of object literals 47167 * such as: {key:"fieldname",parser:YAHOO.util.DataSourceBase.parseDate}</dd> 47168 * <dt>metaFields</dt> <dd>Object literal of keys to include in the oParsedResponse.meta collection</dd> 47169 * <dt>metaNode</dt> <dd>Name of the node under which to search for meta information in XML response data</dd> 47170 * </dl> 47171 * 47172 * @property responseSchema 47173 * @type Object 47174 */ 47175responseSchema : null, 47176 47177/** 47178 * Additional arguments passed to the JSON parse routine. The JSON string 47179 * is the assumed first argument (where applicable). This property is not 47180 * set by default, but the parse methods will use it if present. 47181 * 47182 * @property parseJSONArgs 47183 * @type {MIXED|Array} If an Array, contents are used as individual arguments. 47184 * Otherwise, value is used as an additional argument. 47185 */ 47186// property intentionally undefined 47187 47188/** 47189 * When working with XML data, setting this property to true enables support for 47190 * XPath-syntaxed locators in schema definitions. 47191 * 47192 * @property useXPath 47193 * @type Boolean 47194 * @default false 47195 */ 47196useXPath : false, 47197 47198/** 47199 * Clones entries before adding to cache. 47200 * 47201 * @property cloneBeforeCaching 47202 * @type Boolean 47203 * @default false 47204 */ 47205cloneBeforeCaching : false, 47206 47207///////////////////////////////////////////////////////////////////////////// 47208// 47209// DataSourceBase public methods 47210// 47211///////////////////////////////////////////////////////////////////////////// 47212 47213/** 47214 * Public accessor to the unique name of the DataSource instance. 47215 * 47216 * @method toString 47217 * @return {String} Unique name of the DataSource instance. 47218 */ 47219toString : function() { 47220 return this._sName; 47221}, 47222 47223/** 47224 * Overridable method passes request to cache and returns cached response if any, 47225 * refreshing the hit in the cache as the newest item. Returns null if there is 47226 * no cache hit. 47227 * 47228 * @method getCachedResponse 47229 * @param oRequest {Object} Request object. 47230 * @param oCallback {Object} Callback object. 47231 * @param oCaller {Object} (deprecated) Use callback object. 47232 * @return {Object} Cached response object or null. 47233 */ 47234getCachedResponse : function(oRequest, oCallback, oCaller) { 47235 var aCache = this._aCache; 47236 47237 // If cache is enabled... 47238 if(this.maxCacheEntries > 0) { 47239 // Initialize local cache 47240 if(!aCache) { 47241 this._aCache = []; 47242 YAHOO.log("Cache initialized", "info", this.toString()); 47243 } 47244 // Look in local cache 47245 else { 47246 var nCacheLength = aCache.length; 47247 if(nCacheLength > 0) { 47248 var oResponse = null; 47249 this.fireEvent("cacheRequestEvent", {request:oRequest,callback:oCallback,caller:oCaller}); 47250 47251 // Loop through each cached element 47252 for(var i = nCacheLength-1; i >= 0; i--) { 47253 var oCacheElem = aCache[i]; 47254 47255 // Defer cache hit logic to a public overridable method 47256 if(this.isCacheHit(oRequest,oCacheElem.request)) { 47257 // The cache returned a hit! 47258 // Grab the cached response 47259 oResponse = oCacheElem.response; 47260 this.fireEvent("cacheResponseEvent", {request:oRequest,response:oResponse,callback:oCallback,caller:oCaller}); 47261 47262 // Refresh the position of the cache hit 47263 if(i < nCacheLength-1) { 47264 // Remove element from its original location 47265 aCache.splice(i,1); 47266 // Add as newest 47267 this.addToCache(oRequest, oResponse); 47268 YAHOO.log("Refreshed cache position of the response for \"" + oRequest + "\"", "info", this.toString()); 47269 } 47270 47271 // Add a cache flag 47272 oResponse.cached = true; 47273 break; 47274 } 47275 } 47276 YAHOO.log("The cached response for \"" + lang.dump(oRequest) + 47277 "\" is " + lang.dump(oResponse), "info", this.toString()); 47278 return oResponse; 47279 } 47280 } 47281 } 47282 else if(aCache) { 47283 this._aCache = null; 47284 YAHOO.log("Cache destroyed", "info", this.toString()); 47285 } 47286 return null; 47287}, 47288 47289/** 47290 * Default overridable method matches given request to given cached request. 47291 * Returns true if is a hit, returns false otherwise. Implementers should 47292 * override this method to customize the cache-matching algorithm. 47293 * 47294 * @method isCacheHit 47295 * @param oRequest {Object} Request object. 47296 * @param oCachedRequest {Object} Cached request object. 47297 * @return {Boolean} True if given request matches cached request, false otherwise. 47298 */ 47299isCacheHit : function(oRequest, oCachedRequest) { 47300 return (oRequest === oCachedRequest); 47301}, 47302 47303/**
47304 * Adds a new item to the cache. If cache is full, evicts the stalest item 47305 * before adding the new item. 47306 * 47307 * @method addToCache 47308 * @param oRequest {Object} Request object. 47309 * @param oResponse {Object} Response object to cache. 47310 */ 47311addToCache : function(oRequest, oResponse) { 47312 var aCache = this._aCache; 47313 if(!aCache) { 47314 return; 47315 } 47316 47317 // If the cache is full, make room by removing stalest element (index=0) 47318 while(aCache.length >= this.maxCacheEntries) { 47319 aCache.shift(); 47320 } 47321 47322 // Add to cache in the newest position, at the end of the array 47323 oResponse = (this.cloneBeforeCaching) ? DS._cloneObject(oResponse) : oResponse; 47324 var oCacheElem = {request:oRequest,response:oResponse}; 47325 aCache[aCache.length] = oCacheElem; 47326 this.fireEvent("responseCacheEvent", {request:oRequest,response:oResponse}); 47327 YAHOO.log("Cached the response for \"" + oRequest + "\"", "info", this.toString()); 47328}, 47329 47330/** 47331 * Flushes cache. 47332 * 47333 * @method flushCache 47334 */ 47335flushCache : function() { 47336 if(this._aCache) { 47337 this._aCache = []; 47338 this.fireEvent("cacheFlushEvent"); 47339 YAHOO.log("Flushed the cache", "info", this.toString()); 47340 } 47341}, 47342 47343/** 47344 * Sets up a polling mechanism to send requests at set intervals and forward 47345 * responses to given callback. 47346 * 47347 * @method setInterval 47348 * @param nMsec {Number} Length of interval in milliseconds. 47349 * @param oRequest {Object} Request object. 47350 * @param oCallback {Function} Handler function to receive the response. 47351 * @param oCaller {Object} (deprecated) Use oCallback.scope. 47352 * @return {Number} Interval ID. 47353 */ 47354setInterval : function(nMsec, oRequest, oCallback, oCaller) { 47355 if(lang.isNumber(nMsec) && (nMsec >= 0)) { 47356 YAHOO.log("Enabling polling to live data for \"" + oRequest + "\" at interval " + nMsec, "info", this.toString()); 47357 var oSelf = this; 47358 var nId = setInterval(function() { 47359 oSelf.makeConnection(oRequest, oCallback, oCaller); 47360 }, nMsec); 47361 this._aIntervals.push(nId); 47362 return nId; 47363 } 47364 else { 47365 YAHOO.log("Could not enable polling to live data for \"" + oRequest + "\" at interval " + nMsec, "info", this.toString()); 47366 } 47367}, 47368 47369/** 47370 * Disables polling mechanism associated with the given interval ID. Does not 47371 * affect transactions that are in progress. 47372 * 47373 * @method clearInterval 47374 * @param nId {Number} Interval ID. 47375 */ 47376clearInterval : function(nId) { 47377 // Remove from tracker if there 47378 var tracker = this._aIntervals || []; 47379 for(var i=tracker.length-1; i>-1; i--) { 47380 if(tracker[i] === nId) { 47381 tracker.splice(i,1); 47382 clearInterval(nId); 47383 } 47384 } 47385}, 47386 47387/** 47388 * Disables all known polling intervals. Does not affect transactions that are 47389 * in progress. 47390 * 47391 * @method clearAllIntervals 47392 */ 47393clearAllIntervals : function() { 47394 var tracker = this._aIntervals || []; 47395 for(var i=tracker.length-1; i>-1; i--) { 47396 clearInterval(tracker[i]); 47397 } 47398 tracker = []; 47399}, 47400 47401/** 47402 * First looks for cached response, then sends request to live data. The 47403 * following arguments are passed to the callback function: 47404 * <dl> 47405 * <dt><code>oRequest</code></dt> 47406 * <dd>The same value that was passed in as the first argument to sendRequest.</dd> 47407 * <dt><code>oParsedResponse</code></dt> 47408 * <dd>An object literal containing the following properties: 47409 * <dl> 47410 * <dt><code>tId</code></dt> 47411 * <dd>Unique transaction ID number.</dd> 47412 * <dt><code>results</code></dt> 47413 * <dd>Schema-parsed data results.</dd> 47414 * <dt><code>error</code></dt> 47415 * <dd>True in cases of data error.</dd> 47416 * <dt><code>cached</code></dt> 47417 * <dd>True when response is returned from DataSource cache.</dd> 47418 * <dt><code>meta</code></dt> 47419 * <dd>Schema-parsed meta data.</dd> 47420 * </dl> 47421 * <dt><code>oPayload</code></dt> 47422 * <dd>The same value as was passed in as <code>argument</code> in the oCallback object literal.</dd> 47423 * </dl> 47424 * 47425 * @method sendRequest 47426 * @param oRequest {Object} Request object. 47427 * @param oCallback {Object} An object literal with the following properties: 47428 * <dl> 47429 * <dt><code>success</code></dt> 47430 * <dd>The function to call when the data is ready.</dd> 47431 * <dt><code>failure</code></dt> 47432 * <dd>The function to call upon a response failure condition.</dd> 47433 * <dt><code>scope</code></dt> 47434 * <dd>The object to serve as the scope for the success and failure handlers.</dd> 47435 * <dt><code>argument</code></dt> 47436 * <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
47437 * </dl> 47438 * @param oCaller {Object} (deprecated) Use oCallback.scope. 47439 * @return {Number} Transaction ID, or null if response found in cache. 47440 */ 47441sendRequest : function(oRequest, oCallback, oCaller) { 47442 // First look in cache 47443 var oCachedResponse = this.getCachedResponse(oRequest, oCallback, oCaller); 47444 if(oCachedResponse) { 47445 DS.issueCallback(oCallback,[oRequest,oCachedResponse],false,oCaller); 47446 return null; 47447 } 47448 47449 47450 // Not in cache, so forward request to live data 47451 YAHOO.log("Making connection to live data for \"" + oRequest + "\"", "info", this.toString()); 47452 return this.makeConnection(oRequest, oCallback, oCaller); 47453}, 47454 47455/** 47456 * Overridable default method generates a unique transaction ID and passes 47457 * the live data reference directly to the handleResponse function. This 47458 * method should be implemented by subclasses to achieve more complex behavior 47459 * or to access remote data. 47460 * 47461 * @method makeConnection 47462 * @param oRequest {Object} Request object. 47463 * @param oCallback {Object} Callback object literal. 47464 * @param oCaller {Object} (deprecated) Use oCallback.scope. 47465 * @return {Number} Transaction ID. 47466 */ 47467makeConnection : function(oRequest, oCallback, oCaller) { 47468 var tId = DS._nTransactionId++; 47469 this.fireEvent("requestEvent", {tId:tId, request:oRequest,callback:oCallback,caller:oCaller}); 47470 47471 /* accounts for the following cases: 47472 YAHOO.util.DataSourceBase.TYPE_UNKNOWN 47473 YAHOO.util.DataSourceBase.TYPE_JSARRAY 47474 YAHOO.util.DataSourceBase.TYPE_JSON 47475 YAHOO.util.DataSourceBase.TYPE_HTMLTABLE 47476 YAHOO.util.DataSourceBase.TYPE_XML 47477 YAHOO.util.DataSourceBase.TYPE_TEXT 47478 */ 47479 var oRawResponse = this.liveData; 47480 47481 this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId); 47482 return tId; 47483}, 47484 47485/** 47486 * Receives raw data response and type converts to XML, JSON, etc as necessary. 47487 * Forwards oFullResponse to appropriate parsing function to get turned into 47488 * oParsedResponse. Calls doBeforeCallback() and adds oParsedResponse to 47489 * the cache when appropriate before calling issueCallback(). 47490 * 47491 * The oParsedResponse object literal has the following properties: 47492 * <dl> 47493 * <dd><dt>tId {Number}</dt> Unique transaction ID</dd> 47494 * <dd><dt>results {Array}</dt> Array of parsed data results</dd> 47495 * <dd><dt>meta {Object}</dt> Object literal of meta values</dd> 47496 * <dd><dt>error {Boolean}</dt> (optional) True if there was an error</dd> 47497 * <dd><dt>cached {Boolean}</dt> (optional) True if response was cached</dd> 47498 * </dl> 47499 * 47500 * @method handleResponse 47501 * @param oRequest {Object} Request object 47502 * @param oRawResponse {Object} The raw response from the live database. 47503 * @param oCallback {Object} Callback object literal. 47504 * @param oCaller {Object} (deprecated) Use oCallback.scope. 47505 * @param tId {Number} Transaction ID. 47506 */ 47507handleResponse : function(oRequest, oRawResponse, oCallback, oCaller, tId) { 47508 this.fireEvent("responseEvent", {tId:tId, request:oRequest, response:oRawResponse, 47509 callback:oCallback, caller:oCaller}); 47510 YAHOO.log("Received live data response for \"" + oRequest + "\"", "info", this.toString()); 47511 var xhr = (this.dataType == DS.TYPE_XHR) ? true : false; 47512 var oParsedResponse = null; 47513 var oFullResponse = oRawResponse; 47514 47515 // Try to sniff data type if it has not been defined 47516 if(this.responseType === DS.TYPE_UNKNOWN) { 47517 var ctype = (oRawResponse && oRawResponse.getResponseHeader) ? oRawResponse.getResponseHeader["Content-Type"] : null; 47518 if(ctype) { 47519 // xml 47520 if(ctype.indexOf("text/xml") > -1) { 47521 this.responseType = DS.TYPE_XML; 47522 } 47523 else if(ctype.indexOf("application/json") > -1) { // json 47524 this.responseType = DS.TYPE_JSON; 47525 } 47526 else if(ctype.indexOf("text/plain") > -1) { // text 47527 this.responseType = DS.TYPE_TEXT; 47528 } 47529 } 47530 else { 47531 if(YAHOO.lang.isArray(oRawResponse)) { // array 47532 this.responseType = DS.TYPE_JSARRAY; 47533 } 47534 // xml 47535 else if(oRawResponse && oRawResponse.nodeType && (oRawResponse.nodeType === 9 || oRawResponse.nodeType === 1 || oRawResponse.nodeType === 11)) { 47536 this.responseType = DS.TYPE_XML; 47537 } 47538 else if(oRawResponse && oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table 47539 this.responseType = DS.TYPE_HTMLTABLE; 47540 } 47541 else if(YAHOO.lang.isObject(oRawResponse)) { // json 47542 this.responseType = DS.TYPE_JSON; 47543 } 47544 else if(YAHOO.lang.isString(oRawResponse)) { // text 47545 this.responseType = DS.TYPE_TEXT; 47546 } 47547 } 47548 } 47549 47550 switch(this.responseType) { 47551 case DS.TYPE_JSARRAY: 47552 if(xhr && oRawResponse && oRawResponse.responseText) { 47553 oFullResponse = oRawResponse.responseText; 47554 } 47555 try { 47556 // Convert to JS array if it's a string 47557 if(lang.isString(oFullResponse)) { 47558 var parseArgs = [oFullResponse].concat(this.parseJSONArgs); 47559 // Check for YUI JSON Util 47560 if(lang.JSON) { 47561 oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs); 47562 } 47563 // Look for JSON parsers using an API similar to json2.js 47564 else if(window.JSON && JSON.parse) { 47565 oFullResponse = JSON.parse.apply(JSON,parseArgs); 47566 } 47567 // Look for JSON parsers using an API similar to json.js 47568 else if(oFullResponse.parseJSON) { 47569 oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1)); 47570 } 47571 // No JSON lib found so parse the string 47572 else {
47573 // Trim leading spaces 47574 while (oFullResponse.length > 0 && 47575 (oFullResponse.charAt(0) != "{") && 47576 (oFullResponse.charAt(0) != "[")) { 47577 oFullResponse = oFullResponse.substring(1, oFullResponse.length); 47578 } 47579 47580 if(oFullResponse.length > 0) { 47581 // Strip extraneous stuff at the end 47582 var arrayEnd = 47583Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}")); 47584 oFullResponse = oFullResponse.substring(0,arrayEnd+1); 47585 47586 // Turn the string into an object literal... 47587 // ...eval is necessary here 47588 oFullResponse = eval("(" + oFullResponse + ")"); 47589 47590 } 47591 } 47592 } 47593 } 47594 catch(e1) { 47595 } 47596 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47597 oParsedResponse = this.parseArrayData(oRequest, oFullResponse); 47598 break; 47599 case DS.TYPE_JSON: 47600 if(xhr && oRawResponse && oRawResponse.responseText) { 47601 oFullResponse = oRawResponse.responseText; 47602 } 47603 try { 47604 // Convert to JSON object if it's a string 47605 if(lang.isString(oFullResponse)) { 47606 var parseArgs = [oFullResponse].concat(this.parseJSONArgs); 47607 // Check for YUI JSON Util 47608 if(lang.JSON) { 47609 oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs); 47610 } 47611 // Look for JSON parsers using an API similar to json2.js 47612 else if(window.JSON && JSON.parse) { 47613 oFullResponse = JSON.parse.apply(JSON,parseArgs); 47614 } 47615 // Look for JSON parsers using an API similar to json.js 47616 else if(oFullResponse.parseJSON) { 47617 oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1)); 47618 } 47619 // No JSON lib found so parse the string 47620 else { 47621 // Trim leading spaces 47622 while (oFullResponse.length > 0 && 47623 (oFullResponse.charAt(0) != "{") && 47624 (oFullResponse.charAt(0) != "[")) { 47625 oFullResponse = oFullResponse.substring(1, oFullResponse.length); 47626 } 47627 47628 if(oFullResponse.length > 0) { 47629 // Strip extraneous stuff at the end 47630 var objEnd = Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}")); 47631 oFullResponse = oFullResponse.substring(0,objEnd+1); 47632 47633 // Turn the string into an object literal... 47634 // ...eval is necessary here 47635 oFullResponse = eval("(" + oFullResponse + ")"); 47636 47637 } 47638 } 47639 } 47640 } 47641 catch(e) { 47642 } 47643 47644 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47645 oParsedResponse = this.parseJSONData(oRequest, oFullResponse); 47646 break; 47647 case DS.TYPE_HTMLTABLE: 47648 if(xhr && oRawResponse.responseText) { 47649 var el = document.createElement('div'); 47650 el.innerHTML = oRawResponse.responseText; 47651 oFullResponse = el.getElementsByTagName('table')[0]; 47652 } 47653 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47654 oParsedResponse = this.parseHTMLTableData(oRequest, oFullResponse); 47655 break; 47656 case DS.TYPE_XML: 47657 if(xhr && oRawResponse.responseXML) { 47658 oFullResponse = oRawResponse.responseXML; 47659 } 47660 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47661 oParsedResponse = this.parseXMLData(oRequest, oFullResponse); 47662 break; 47663 case DS.TYPE_TEXT: 47664 if(xhr && lang.isString(oRawResponse.responseText)) { 47665 oFullResponse = oRawResponse.responseText; 47666 } 47667 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47668 oParsedResponse = this.parseTextData(oRequest, oFullResponse); 47669 break; 47670 default: 47671 oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback); 47672 oParsedResponse = this.parseData(oRequest, oFullResponse); 47673 break; 47674 } 47675 47676
47677 // Clean up for consistent signature 47678 oParsedResponse = oParsedResponse || {}; 47679 if(!oParsedResponse.results) { 47680 oParsedResponse.results = []; 47681 } 47682 if(!oParsedResponse.meta) { 47683 oParsedResponse.meta = {}; 47684 } 47685 47686 // Success 47687 if(!oParsedResponse.error) { 47688 // Last chance to touch the raw response or the parsed response 47689 oParsedResponse = this.doBeforeCallback(oRequest, oFullResponse, oParsedResponse, oCallback); 47690 this.fireEvent("responseParseEvent", {request:oRequest, 47691 response:oParsedResponse, callback:oCallback, caller:oCaller}); 47692 // Cache the response 47693 this.addToCache(oRequest, oParsedResponse); 47694 } 47695 // Error 47696 else { 47697 // Be sure the error flag is on 47698 oParsedResponse.error = true; 47699 this.fireEvent("dataErrorEvent", {request:oRequest, response: oRawResponse, callback:oCallback, 47700 caller:oCaller, message:DS.ERROR_DATANULL}); 47701 YAHOO.log(DS.ERROR_DATANULL, "error", this.toString()); 47702 } 47703 47704 // Send the response back to the caller 47705 oParsedResponse.tId = tId; 47706 DS.issueCallback(oCallback,[oRequest,oParsedResponse],oParsedResponse.error,oCaller); 47707}, 47708 47709/** 47710 * Overridable method gives implementers access to the original full response 47711 * before the data gets parsed. Implementers should take care not to return an 47712 * unparsable or otherwise invalid response. 47713 * 47714 * @method doBeforeParseData 47715 * @param oRequest {Object} Request object. 47716 * @param oFullResponse {Object} The full response from the live database. 47717 * @param oCallback {Object} The callback object. 47718 * @return {Object} Full response for parsing. 47719 47720 */ 47721doBeforeParseData : function(oRequest, oFullResponse, oCallback) { 47722 return oFullResponse; 47723}, 47724 47725/** 47726 * Overridable method gives implementers access to the original full response and 47727 * the parsed response (parsed against the given schema) before the data 47728 * is added to the cache (if applicable) and then sent back to callback function. 47729 * This is your chance to access the raw response and/or populate the parsed 47730 * response with any custom data. 47731 * 47732 * @method doBeforeCallback 47733 * @param oRequest {Object} Request object. 47734 * @param oFullResponse {Object} The full response from the live database. 47735 * @param oParsedResponse {Object} The parsed response to return to calling object. 47736 * @param oCallback {Object} The callback object. 47737 * @return {Object} Parsed response object. 47738 */ 47739doBeforeCallback : function(oRequest, oFullResponse, oParsedResponse, oCallback) { 47740 return oParsedResponse; 47741}, 47742 47743/** 47744 * Overridable method parses data of generic RESPONSE_TYPE into a response object. 47745 * 47746 * @method parseData 47747 * @param oRequest {Object} Request object. 47748 * @param oFullResponse {Object} The full Array from the live database. 47749 * @return {Object} Parsed response object with the following properties:<br> 47750 * - results {Array} Array of parsed data results<br> 47751 * - meta {Object} Object literal of meta values<br> 47752 * - error {Boolean} (optional) True if there was an error<br> 47753 */ 47754parseData : function(oRequest, oFullResponse) { 47755 if(lang.isValue(oFullResponse)) { 47756 var oParsedResponse = {results:oFullResponse,meta:{}}; 47757 YAHOO.log("Parsed generic data is " + 47758 lang.dump(oParsedResponse), "info", this.toString()); 47759 return oParsedResponse; 47760 47761 } 47762 YAHOO.log("Generic data could not be parsed: " + lang.dump(oFullResponse), 47763 "error", this.toString()); 47764 return null; 47765}, 47766 47767/** 47768 * Overridable method parses Array data into a response object. 47769 * 47770 * @method parseArrayData 47771 * @param oRequest {Object} Request object. 47772 * @param oFullResponse {Object} The full Array from the live database. 47773 * @return {Object} Parsed response object with the following properties:<br> 47774 * - results (Array) Array of parsed data results<br> 47775 * - error (Boolean) True if there was an error 47776 */ 47777parseArrayData : function(oRequest, oFullResponse) { 47778 if(lang.isArray(oFullResponse)) { 47779 var results = [], 47780 i, j, 47781 rec, field, data; 47782 47783 // Parse for fields 47784 if(lang.isArray(this.responseSchema.fields)) { 47785 var fields = this.responseSchema.fields; 47786 for (i = fields.length - 1; i >= 0; --i) { 47787 if (typeof fields[i] !== 'object') { 47788 fields[i] = { key : fields[i] }; 47789 } 47790 } 47791 47792 var parsers = {}, p; 47793 for (i = fields.length - 1; i >= 0; --i) { 47794 p = (typeof fields[i].parser === 'function' ? 47795 fields[i].parser : 47796 DS.Parser[fields[i].parser+'']) || fields[i].converter; 47797 if (p) { 47798 parsers[fields[i].key] = p; 47799 } 47800 } 47801 47802 var arrType = lang.isArray(oFullResponse[0]); 47803 for(i=oFullResponse.length-1; i>-1; i--) { 47804 var oResult = {}; 47805 rec = oFullResponse[i]; 47806 if (typeof rec === 'object') { 47807 for(j=fields.length-1; j>-1; j--) { 47808 field = fields[j]; 47809 data = arrType ? rec[j] : rec[field.key]; 47810 47811 if (parsers[field.key]) { 47812 data = parsers[field.key].call(this,data); 47813 } 47814 47815 // Safety measure 47816 if(data === undefined) { 47817 data = null; 47818 } 47819 47820 oResult[field.key] = data; 47821 } 47822 } 47823 else if (lang.isString(rec)) { 47824 for(j=fields.length-1; j>-1; j--) { 47825 field = fields[j]; 47826 data = rec; 47827 47828 if (parsers[field.key]) { 47829 data = parsers[field.key].call(this,data); 47830 } 47831 47832 // Safety measure 47833 if(data === undefined) { 47834 data = null; 47835 } 47836 47837 oResult[field.key] = data; 47838 } 47839 } 47840 results[i] = oResult; 47841 } 47842 } 47843 // Return entire data set 47844 else { 47845 results = oFullResponse; 47846 } 47847 var oParsedResponse = {results:results}; 47848 YAHOO.log("Parsed array data is " + 47849 lang.dump(oParsedResponse), "info", this.toString()); 47850 return oParsedResponse; 47851 47852 } 47853 YAHOO.log("Array data could not be parsed: " + lang.dump(oFullResponse), 47854 "error", this.toString()); 47855 return null; 47856}, 47857 47858/** 47859 * Overridable method parses plain text data into a response object. 47860 * 47861 * @method parseTextData 47862 * @param oRequest {Object} Request object. 47863 * @param oFullResponse {Object} The full text response from the live database. 47864 * @return {Object} Parsed response object with the following properties:<br> 47865 * - results (Array) Array of parsed data results<br> 47866 * - error (Boolean) True if there was an error 47867 */ 47868parseTextData : function(oRequest, oFullResponse) { 47869 if(lang.isString(oFullResponse)) { 47870 if(lang.isString(this.responseSchema.recordDelim) && 47871 lang.isString(this.responseSchema.fieldDelim)) { 47872 var oParsedResponse = {results:[]}; 47873 var recDelim = this.responseSchema.recordDelim; 47874 var fieldDelim = this.responseSchema.fieldDelim; 47875 if(oFullResponse.length > 0) { 47876 // Delete the last line delimiter at the end of the data if it exists 47877 var newLength = oFullResponse.length-recDelim.length; 47878 if(oFullResponse.substr(newLength) == recDelim) { 47879 oFullResponse = oFullResponse.substr(0, newLength); 47880 } 47881 if(oFullResponse.length > 0) { 47882 // Split along record delimiter to get an array of str
47882ings 47883 var recordsarray = oFullResponse.split(recDelim); 47884 // Cycle through each record 47885 for(var i = 0, len = recordsarray.length, recIdx = 0; i < len; ++i) { 47886 var bError = false, 47887 sRecord = recordsarray[i]; 47888 if (lang.isString(sRecord) && (sRecord.length > 0)) { 47889 // Split each record along field delimiter to get data 47890 var fielddataarray = recordsarray[i].split(fieldDelim); 47891 var oResult = {}; 47892 47893 // Filter for fields data 47894 if(lang.isArray(this.responseSchema.fields)) { 47895 var fields = this.responseSchema.fields; 47896 for(var j=fields.length-1; j>-1; j--) { 47897 try { 47898 // Remove quotation marks from edges, if applicable 47899 var data = fielddataarray[j]; 47900 if (lang.isString(data)) { 47901 if(data.charAt(0) == "\"") { 47902 data = data.substr(1); 47903 } 47904 if(data.charAt(data.length-1) == "\"") { 47905 data = data.substr(0,data.length-1); 47906 } 47907 var field = fields[j]; 47908 var key = (lang.isValue(field.key)) ? field.key : field; 47909 // Backward compatibility 47910 if(!field.parser && field.converter) { 47911 field.parser = field.converter; 47912 YAHOO.log("The field property converter has been deprecated" + 47913 " in favor of parser", "warn", this.toString()); 47914 } 47915 var parser = (typeof field.parser === 'function') ? 47916 field.parser : 47917 DS.Parser[field.parser+'']; 47918 if(parser) { 47919 data = parser.call(this, data); 47920 } 47921 // Safety measure 47922 if(data === undefined) { 47923 data = null; 47924 } 47925 oResult[key] = data; 47926 } 47927 else { 47928 bError = true; 47929 } 47930 } 47931 catch(e) { 47932 bError = true; 47933 } 47934 } 47935 } 47936 // No fields defined so pass along all data as an array 47937 else { 47938 oResult = fielddataarray; 47939 } 47940 if(!bError) { 47941 oParsedResponse.results[recIdx++] = oResult; 47942 } 47943 } 47944 } 47945 } 47946 } 47947 YAHOO.log("Parsed text data is " + 47948 lang.dump(oParsedResponse), "info", this.toString()); 47949 return oParsedResponse; 47950 } 47951 } 47952 YAHOO.log("Text data could not be parsed: " + lang.dump(oFullResponse), 47953 "error", this.toString()); 47954 return null; 47955 47956}, 47957 47958/** 47959 * Overridable method parses XML data for one result into an object literal. 47960 * 47961 * @method parseXMLResult 47962 * @param result {XML} XML for one result. 47963 * @return {Object} Object literal of data for one result. 47964 */ 47965parseXMLResult : function(result) { 47966 var oResult = {}, 47967 schema = this.responseSchema; 47968 47969 try { 47970 // Loop through each data field in each result using the schema 47971 for(var m = schema.fields.length-1; m >= 0 ; m--) { 47972 var field = schema.fields[m]; 47973 var key = (lang.isValue(field.key)) ? field.key : field; 47974 var data = null; 47975 47976 if(this.useXPath) { 47977 data = YAHOO.util.DataSource._getLocationValue(field, result); 47978 } 47979 else { 47980 // Values may be held in an attribute... 47981 var xmlAttr = result.attributes.getNamedItem(key); 47982 if(xmlAttr) { 47983 data = xmlAttr.value; 47984 } 47985 // ...or in a node 47986 else { 47987 var xmlNode = result.getElementsByTagName(key); 47988 if(xmlNode && xmlNode.item(0)) { 47989 var item = xmlNode.item(0); 47990 // For IE, then DOM... 47991 data = (item) ? ((item.text) ? item.text : (item.textContent) ? item.textContent : null) : null; 47992 // ...then fallback, but check for multiple child nodes 47993 if(!data) { 47994 var datapieces = []; 47995 for(var j=0, len=item.childNodes.length; j<len; j++) { 47996 if(item.childNodes[j].nodeValue) { 47997 datapieces[datapieces.length] = item.childNodes[j].nodeValue; 47998 } 47999 } 48000 if(datapieces.length > 0) { 48001 data = datapieces.join(""); 48002 } 48003 } 48004 } 48005 } 48006 } 48007 48008 48009 // Safety net 48010 if(data === null) { 48011 data = ""; 48012 } 48013 // Backward compatibility 48014 if(!field.parser && field.converter) { 48015 field.parser = field.converter; 48016 YAHOO.log("The field property converter has been deprecated" + 48017 " in favor of parser", "warn", this.toString()); 48018 } 48019 var parser = (typeof field.parser === 'function') ? 48020 field.parser : 48021 DS.Parser[field.parser+'']; 48022 if(parser) { 48023 data = parser.call(this, data); 48024 } 48025 // Safety measure 48026 if(data === undefined) { 48027 data = null; 48028 } 48029 oResult[key] = data; 48030 } 48031 } 48032 catch(e) { 48033 YAHOO.log("Error while parsing XML result: " + e.message); 48034 } 48035 48036 return oResult; 48037}, 48038 48039 48040 48041/** 48042 * Overridable method parses XML data into a response object. 48043 * 48044 * @method parseXMLData 48045 * @param oRequest {Object} Request object. 48046 * @param oFullResponse {Object} The full XML response from the live database. 48047 * @return {Object} Parsed response object with the following properties<br> 48048 * - results (Array) Array of parsed data results<br> 48049 * - error (Boolean) True if there was an error 48050 */ 48051parseXMLData : function(oRequest, oFullResponse) { 48052 var bError = false, 48053 schema = this.responseSchema, 48054 oParsedResponse = {meta:{}}, 48055 xmlList = null, 48056 metaNode = schema.metaNode, 48057 metaLocators = schema.metaFields || {}, 48058 i,k,loc,v; 48059 48060 // In case oFullResponse is something funky 48061 try { 48062 // Pull any meta identified 48063 if(this.useXPath) { 48064 for (k in metaLocators) { 48065 oParsedResponse.meta[k] = YAHOO.util.DataSource._getLocationValue(metaLocators[k], oFullResponse); 48066 } 48067 } 48068 else { 48069 metaNode = metaNode ? oFullResponse.getElementsByTagName(metaNode)[0] : 48070 oFullResponse; 48071 48072 if (metaNode) { 48073 for (k in metaLocators) { 48074 if (lang.hasOwnProperty(metaLocators, k)) { 48075 loc = metaLocators[k]; 48076 // Look for a node 48077 v = metaNode.getElementsByTagName(loc)[0]; 48078 48079 if (v) { 48080 v = v.firstChild.nodeValue; 48081 } else { 48082 // Look for an attribute 48083 v = metaNode.attributes.getNamedItem(loc); 48084 if (v) { 48085 v = v.value; 48086 } 48087 } 48088 48089 if (lang.isValue(v)) { 48090 oParsedResponse.meta[k] = v; 48091 } 48092 } 48093 } 48094 } 48095 } 48096 48097 // For result data 48098 xmlList = (schema.resultNode) ? 48099 oFullResponse.getElementsByTagName(schema.resultNode) : 48100 null; 48101 } 48102 catch(e) { 48103 YAHOO.log("Error while parsing XML data: " + e.message); 48104 } 48105 if(!xmlList || !lang.isArray(schema.fields)) { 48106 bError = true; 48107 } 48108 // Loop through each result 48109 else { 48110 oParsedResponse.results = []; 48111 for(i = xmlList.length-1; i >= 0 ; --i) { 48112 var oResult = this.parseXMLResult(xmlList.item(i)); 48113 // Capture each array of values into an array of results 48114 oParsedResponse.results[i] = oResult; 48115 } 48116 } 48117 if(bError) { 48118 YAHOO.log("XML data could not be parsed: " + 48119 lang.dump(oFullResponse), "error", this.toString()); 48120 oParsedResponse.error = true; 48121 } 48122 else { 48123 YAHOO.log("Parsed XML data is " + 48124 lang.dump(oParsedResponse), "info", this.toString()); 48125 } 48126 return oParsedResponse; 48127}, 48128 48129/** 48130 * Overridable method parses JSON data into a response object. 48131 * 48132 * @method parseJSONData 48133 * @param oRequest {Object} Request object. 48134 * @param oFullResponse {Object} The full JSON from the live database. 48135 * @return {Object} Parsed response object with the following properties<br> 48136 * - results (Array) Array of parsed data results<br> 48137 * - error (Boolean) True if there was an error 48138 */ 48139parseJSONData : function(oRequest, oFullResponse) { 48140 var oParsedResponse = {results:[],meta:{}}; 48141 48142 if(lang.isObject(oFullResponse) && this.responseSchema.resultsList) { 48143 var schema = this.responseSchema, 48144 fields = schema.fields, 48145 resultsList = oFullResponse, 48146 results = [], 48147 metaFields = schema.metaFields || {}, 48148 fieldParsers = [], 48149 fieldPaths = [], 48150 simpleFields = [], 48151 bError = false, 48152 i,len,j,v,key,parser,path; 48153 48154 // Function to convert the schema's fields into walk paths 48155 var buildPath = function (needle) { 48156 var path = null, keys = [], i = 0; 48157 if (needle) { 48158 // Strip the ["string keys"] and [1] array indexes 48159 needle = needle. 48160 replace(/\[(['"])(.*?)\1\]/g, 48161 function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}). 48162 replace(/\[(\d+)\]/g, 48163 function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}). 48164 replace(/^\./,''); // remove leading dot 48165
48166 // If the cleaned needle contains invalid characters, the 48167 // path is invalid 48168 if (!/[^\w\.\$@]/.test(needle)) { 48169 path = needle.split('.'); 48170 for (i=path.length-1; i >= 0; --i) { 48171 if (path[i].charAt(0) === '@') { 48172 path[i] = keys[parseInt(path[i].substr(1),10)]; 48173 } 48174 } 48175 } 48176 else { 48177 YAHOO.log("Invalid locator: " + needle, "error", this.toString()); 48178 } 48179 } 48180 return path; 48181 }; 48182 48183 48184 // Function to walk a path and return the pot of gold 48185 var walkPath = function (path, origin) { 48186 var v=origin,i=0,len=path.length; 48187 for (;i<len && v;++i) { 48188 v = v[path[i]]; 48189 } 48190 return v; 48191 }; 48192 48193 // Parse the response 48194 // Step 1. Pull the resultsList from oFullResponse (default assumes 48195 // oFullResponse IS the resultsList) 48196 path = buildPath(schema.resultsList); 48197 if (path) { 48198 resultsList = walkPath(path, oFullResponse); 48199 if (resultsList === undefined) { 48200 bError = true; 48201 } 48202 } else { 48203 bError = true; 48204 } 48205 48206 if (!resultsList) { 48207 resultsList = []; 48208 } 48209 48210 if (!lang.isArray(resultsList)) { 48211 resultsList = [resultsList]; 48212 } 48213 48214 if (!bError) { 48215 // Step 2. Parse out field data if identified 48216 if(schema.fields) { 48217 var field; 48218 // Build the field parser map and location paths 48219 for (i=0, len=fields.length; i<len; i++) { 48220 field = fields[i]; 48221 key = field.key || field; 48222 parser = ((typeof field.parser === 'function') ? 48223 field.parser : 48224 DS.Parser[field.parser+'']) || field.converter; 48225 path = buildPath(key); 48226 48227 if (parser) { 48228 fieldParsers[fieldParsers.length] = {key:key,parser:parser}; 48229 } 48230 48231 if (path) { 48232 if (path.length > 1) { 48233 fieldPaths[fieldPaths.length] = {key:key,path:path}; 48234 } else { 48235 simpleFields[simpleFields.length] = {key:key,path:path[0]}; 48236 } 48237 } else { 48238 YAHOO.log("Invalid key syntax: " + key,"warn",this.toString()); 48239 } 48240 } 48241 48242 // Process the results, flattening the records and/or applying parsers if needed 48243 for (i = resultsList.length - 1; i >= 0; --i) { 48244 var r = resultsList[i], rec = {}; 48245 if(r) { 48246 for (j = simpleFields.length - 1; j >= 0; --j) { 48247 // Bug 1777850: data might be held in an array 48248 rec[simpleFields[j].key] = 48249 (r[simpleFields[j].path] !== undefined) ? 48250 r[simpleFields[j].path] : r[j]; 48251 } 48252 48253 for (j = fieldPaths.length - 1; j >= 0; --j) { 48254 rec[fieldPaths[j].key] = walkPath(fieldPaths[j].path,r); 48255 } 48256 48257 for (j = fieldParsers.length - 1; j >= 0; --j) { 48258 var p = fieldParsers[j].key; 48259 rec[p] = fieldParsers[j].parser.call(this, rec[p]); 48260 if (rec[p] === undefined) { 48261 rec[p] = null; 48262 } 48263 } 48264 } 48265 results[i] = rec; 48266 } 48267 } 48268 else { 48269 results = resultsList; 48270 } 48271 48272 for (key in metaFields) { 48273 if (lang.hasOwnProperty(metaFields,key)) { 48274 path = buildPath(metaFields[key]); 48275 if (path) { 48276 v = walkPath(path, oFullResponse); 48277 oParsedResponse.meta[key] = v; 48278 } 48279 } 48280 } 48281 48282 } else { 48283 YAHOO.log("JSON data could not be parsed due to invali
48283d responseSchema.resultsList or invalid response: " + 48284 lang.dump(oFullResponse), "error", this.toString()); 48285 48286 oParsedResponse.error = true; 48287 } 48288 48289 oParsedResponse.results = results; 48290 } 48291 else { 48292 YAHOO.log("JSON data could not be parsed: " + 48293 lang.dump(oFullResponse), "error", this.toString()); 48294 oParsedResponse.error = true; 48295 } 48296 48297 return oParsedResponse; 48298}, 48299 48300/** 48301 * Overridable method parses an HTML TABLE element reference into a response object. 48302 * Data is parsed out of TR elements from all TBODY elements. 48303 * 48304 * @method parseHTMLTableData 48305 * @param oRequest {Object} Request object. 48306 * @param oFullResponse {Object} The full HTML element reference from the live database. 48307 * @return {Object} Parsed response object with the following properties<br> 48308 * - results (Array) Array of parsed data results<br> 48309 * - error (Boolean) True if there was an error 48310 */ 48311parseHTMLTableData : function(oRequest, oFullResponse) { 48312 var bError = false; 48313 var elTable = oFullResponse; 48314 var fields = this.responseSchema.fields; 48315 var oParsedResponse = {results:[]}; 48316 48317 if(lang.isArray(fields)) { 48318 // Iterate through each TBODY 48319 for(var i=0; i<elTable.tBodies.length; i++) { 48320 var elTbody = elTable.tBodies[i]; 48321 48322 // Iterate through each TR 48323 for(var j=elTbody.rows.length-1; j>-1; j--) { 48324 var elRow = elTbody.rows[j]; 48325 var oResult = {}; 48326 48327 for(var k=fields.length-1; k>-1; k--) { 48328 var field = fields[k]; 48329 var key = (lang.isValue(field.key)) ? field.key : field; 48330 var data = elRow.cells[k].innerHTML; 48331 48332 // Backward compatibility 48333 if(!field.parser && field.converter) { 48334 field.parser = field.converter; 48335 YAHOO.log("The field property converter has been deprecated" + 48336 " in favor of parser", "warn", this.toString()); 48337 } 48338 var parser = (typeof field.parser === 'function') ? 48339 field.parser : 48340 DS.Parser[field.parser+'']; 48341 if(parser) { 48342 data = parser.call(this, data); 48343 } 48344 // Safety measure 48345 if(data === undefined) { 48346 data = null; 48347 } 48348 oResult[key] = data; 48349 } 48350 oParsedResponse.results[j] = oResult; 48351 } 48352 } 48353 } 48354 else { 48355 bError = true; 48356 YAHOO.log("Invalid responseSchema.fields", "error", this.toString()); 48357 } 48358 48359 if(bError) { 48360 YAHOO.log("HTML TABLE data could not be parsed: " + 48361 lang.dump(oFullResponse), "error", this.toString()); 48362 oParsedResponse.error = true; 48363 } 48364 else { 48365 YAHOO.log("Parsed HTML TABLE data is " + 48366 lang.dump(oParsedResponse), "info", this.toString()); 48367 } 48368 return oParsedResponse; 48369} 48370 48371}; 48372 48373// DataSourceBase uses EventProvider 48374lang.augmentProto(DS, util.EventProvider); 48375 48376 48377 48378/****************************************************************************/ 48379/****************************************************************************/ 48380/****************************************************************************/ 48381 48382/** 48383 * LocalDataSource class for in-memory data structs including JavaScript arrays, 48384 * JavaScript object literals (JSON), XML documents, and HTML tables. 48385 * 48386 * @namespace YAHOO.util 48387 * @class YAHOO.util.LocalDataSource 48388 * @extends YAHOO.util.DataSourceBase 48389 * @constructor 48390 * @param oLiveData {HTMLElement} Pointer to live data. 48391 * @param oConfigs {object} (optional) Object literal of configuration values. 48392 */ 48393util.LocalDataSource = function(oLiveData, oConfigs) { 48394 this.dataType = DS.TYPE_LOCAL; 48395 48396 if(oLiveData) { 48397 if(YAHOO.lang.isArray(oLiveData)) { // array 48398 this.responseType = DS.TYPE_JSARRAY; 48399 } 48400 // xml 48401 else if(oLiveData.nodeType && oLiveData.nodeType == 9) { 48402 this.responseType = DS.TYPE_XML; 48403 } 48404 else if(oLiveData.nodeName && (oLiveData.nodeName.toLowerCase() == "table")) { // table 48405 this.responseType = DS.TYPE_HTMLTABLE; 48406 oLiveData = oLiveData.cloneNode(true); 48407 } 48408 else if(YAHOO.lang.isString(oLiveData)) { // text 48409 this.responseType = DS.TYPE_TEXT; 48410 } 48411 else if(YAHOO.lang.isObject(oLiveData)) { // json 48412 this.responseType = DS.TYPE_JSON; 48413 } 48414 } 48415 else { 48416 oLiveData = []; 48417 this.responseType = DS.TYPE_JSARRAY; 48418 } 48419 48420 util.LocalDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 48421}; 48422 48423// LocalDataSource extends DataSourceBase 48424lang.extend(util.LocalDataSource, DS); 48425 48426// Copy static members to LocalDataSource class 48427lang.augmentObject(util.LocalDataSource, DS); 48428 48429 48430 48431 48432 48433 48434 48435 48436 48437 48438 48439 48440 48441/****************************************************************************/ 48442/****************************************************************************/ 48443/****************************************************************************/ 48444 48445/**
48446 * FunctionDataSource class for JavaScript functions. 48447 * 48448 * @namespace YAHOO.util 48449 * @class YAHOO.util.FunctionDataSource 48450 * @extends YAHOO.util.DataSourceBase 48451 * @constructor 48452 * @param oLiveData {HTMLElement} Pointer to live data. 48453 * @param oConfigs {object} (optional) Object literal of configuration values. 48454 */ 48455util.FunctionDataSource = function(oLiveData, oConfigs) { 48456 this.dataType = DS.TYPE_JSFUNCTION; 48457 oLiveData = oLiveData || function() {}; 48458 48459 util.FunctionDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 48460}; 48461 48462// FunctionDataSource extends DataSourceBase 48463lang.extend(util.FunctionDataSource, DS, { 48464 48465///////////////////////////////////////////////////////////////////////////// 48466// 48467// FunctionDataSource public properties 48468// 48469///////////////////////////////////////////////////////////////////////////// 48470 48471/** 48472 * Context in which to execute the function. By default, is the DataSource 48473 * instance itself. If set, the function will receive the DataSource instance 48474 * as an additional argument. 48475 * 48476 * @property scope 48477 * @type Object 48478 * @default null 48479 */ 48480scope : null, 48481 48482 48483///////////////////////////////////////////////////////////////////////////// 48484// 48485// FunctionDataSource public methods 48486// 48487///////////////////////////////////////////////////////////////////////////// 48488 48489/** 48490 * Overriding method passes query to a function. The returned response is then 48491 * forwarded to the handleResponse function. 48492 * 48493 * @method makeConnection 48494 * @param oRequest {Object} Request object. 48495 * @param oCallback {Object} Callback object literal. 48496 * @param oCaller {Object} (deprecated) Use oCallback.scope. 48497 * @return {Number} Transaction ID. 48498 */ 48499makeConnection : function(oRequest, oCallback, oCaller) { 48500 var tId = DS._nTransactionId++; 48501 this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller}); 48502 48503 // Pass the request in as a parameter and 48504 // forward the return value to the handler 48505 48506 48507 var oRawResponse = (this.scope) ? this.liveData.call(this.scope, oRequest, this, oCallback) : this.liveData(oRequest, oCallback); 48508 48509 // Try to sniff data type if it has not been defined 48510 if(this.responseType === DS.TYPE_UNKNOWN) { 48511 if(YAHOO.lang.isArray(oRawResponse)) { // array 48512 this.responseType = DS.TYPE_JSARRAY; 48513 } 48514 // xml 48515 else if(oRawResponse && oRawResponse.nodeType && oRawResponse.nodeType == 9) { 48516 this.responseType = DS.TYPE_XML; 48517 } 48518 else if(oRawResponse && oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table 48519 this.responseType = DS.TYPE_HTMLTABLE; 48520 } 48521 else if(YAHOO.lang.isObject(oRawResponse)) { // json 48522 this.responseType = DS.TYPE_JSON; 48523 } 48524 else if(YAHOO.lang.isString(oRawResponse)) { // text 48525 this.responseType = DS.TYPE_TEXT; 48526 } 48527 } 48528 48529 this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId); 48530 return tId; 48531} 48532 48533}); 48534 48535// Copy static members to FunctionDataSource class 48536lang.augmentObject(util.FunctionDataSource, DS); 48537 48538 48539 48540 48541 48542 48543 48544 48545 48546 48547 48548 48549 48550/****************************************************************************/ 48551/****************************************************************************/ 48552/****************************************************************************/ 48553 48554/** 48555 * ScriptNodeDataSource class for accessing remote data via the YUI Get Utility. 48556 * 48557 * @namespace YAHOO.util 48558 * @class YAHOO.util.ScriptNodeDataSource 48559 * @extends YAHOO.util.DataSourceBase 48560 * @constructor 48561 * @param oLiveData {HTMLElement} Pointer to live data. 48562 * @param oConfigs {object} (optional) Object literal of configuration values. 48563 */ 48564util.ScriptNodeDataSource = function(oLiveData, oConfigs) { 48565 this.dataType = DS.TYPE_SCRIPTNODE; 48566 oLiveData = oLiveData || ""; 48567 48568 util.ScriptNodeDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 48569}; 48570 48571// ScriptNodeDataSource extends DataSourceBase 48572lang.extend(util.ScriptNodeDataSource, DS, { 48573 48574///////////////////////////////////////////////////////////////////////////// 48575// 48576// ScriptNodeDataSource public properties 48577// 48578///////////////////////////////////////////////////////////////////////////// 48579 48580/** 48581 * Alias to YUI Get Utility, to allow implementers to use a custom class. 48582 * 48583 * @property getUtility 48584 * @type Object 48585 * @default YAHOO.util.Get 48586 */ 48587getUtility : util.Get, 48588 48589/** 48590 * Defines request/response management in the following manner: 48591 * <dl> 48592 * <!--<dt>queueRequests</dt> 48593 * <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd> 48594 * <dt>cancelStaleRequests</dt> 48595 * <dd>If a request is already in progress, cancel it before sending the next request.</dd>--> 48596 * <dt>ignoreStaleResponses</dt> 48597 * <dd>Send all requests, but handle only the response for the most recently sent request.</dd> 48598 * <dt>allowAll</dt> 48599 * <dd>Send all requests and handle all responses.</dd> 48600 * </dl> 48601 * 48602 * @property asyncMode
48603 * @type String 48604 * @default "allowAll" 48605 */ 48606asyncMode : "allowAll", 48607 48608/** 48609 * Callback string parameter name sent to the remote script. By default, 48610 * requests are sent to 48611 * <URI>?<scriptCallbackParam>=callback 48612 * 48613 * @property scriptCallbackParam 48614 * @type String 48615 * @default "callback" 48616 */ 48617scriptCallbackParam : "callback", 48618 48619 48620///////////////////////////////////////////////////////////////////////////// 48621// 48622// ScriptNodeDataSource public methods 48623// 48624///////////////////////////////////////////////////////////////////////////// 48625 48626/** 48627 * Creates a request callback that gets appended to the script URI. Implementers 48628 * can customize this string to match their server's query syntax. 48629 * 48630 * @method generateRequestCallback 48631 * @return {String} String fragment that gets appended to script URI that 48632 * specifies the callback function 48633 */ 48634generateRequestCallback : function(id) { 48635 return "&" + this.scriptCallbackParam + "=YAHOO.util.ScriptNodeDataSource.callbacks["+id+"]" ; 48636}, 48637 48638/** 48639 * Overridable method gives implementers access to modify the URI before the dynamic 48640 * script node gets inserted. Implementers should take care not to return an 48641 * invalid URI. 48642 * 48643 * @method doBeforeGetScriptNode 48644 * @param {String} URI to the script 48645 * @return {String} URI to the script 48646 */ 48647doBeforeGetScriptNode : function(sUri) { 48648 return sUri; 48649}, 48650 48651/** 48652 * Overriding method passes query to Get Utility. The returned 48653 * response is then forwarded to the handleResponse function. 48654 * 48655 * @method makeConnection 48656 * @param oRequest {Object} Request object. 48657 * @param oCallback {Object} Callback object literal. 48658 * @param oCaller {Object} (deprecated) Use oCallback.scope. 48659 * @return {Number} Transaction ID. 48660 */ 48661makeConnection : function(oRequest, oCallback, oCaller) { 48662 var tId = DS._nTransactionId++; 48663 this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller}); 48664 48665 // If there are no global pending requests, it is safe to purge global callback stack and global counter 48666 if(util.ScriptNodeDataSource._nPending === 0) { 48667 util.ScriptNodeDataSource.callbacks = []; 48668 util.ScriptNodeDataSource._nId = 0; 48669 } 48670 48671 // ID for this request 48672 var id = util.ScriptNodeDataSource._nId; 48673 util.ScriptNodeDataSource._nId++; 48674 48675 // Dynamically add handler function with a closure to the callback stack 48676 var oSelf = this; 48677 util.ScriptNodeDataSource.callbacks[id] = function(oRawResponse) { 48678 if((oSelf.asyncMode !== "ignoreStaleResponses")|| 48679 (id === util.ScriptNodeDataSource.callbacks.length-1)) { // Must ignore stale responses 48680 48681 // Try to sniff data type if it has not been defined 48682 if(oSelf.responseType === DS.TYPE_UNKNOWN) { 48683 if(YAHOO.lang.isArray(oRawResponse)) { // array 48684 oSelf.responseType = DS.TYPE_JSARRAY; 48685 } 48686 // xml 48687 else if(oRawResponse.nodeType && oRawResponse.nodeType == 9) { 48688 oSelf.responseType = DS.TYPE_XML; 48689 } 48690 else if(oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table 48691 oSelf.responseType = DS.TYPE_HTMLTABLE; 48692 } 48693 else if(YAHOO.lang.isObject(oRawResponse)) { // json 48694 oSelf.responseType = DS.TYPE_JSON; 48695 } 48696 else if(YAHOO.lang.isString(oRawResponse)) { // text 48697 oSelf.responseType = DS.TYPE_TEXT; 48698 } 48699 } 48700 48701 oSelf.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId); 48702 } 48703 else { 48704 YAHOO.log("DataSource ignored stale response for tId " + tId + "(" + oRequest + ")", "info", oSelf.toString()); 48705 } 48706 48707 delete util.ScriptNodeDataSource.callbacks[id]; 48708 }; 48709 48710 // We are now creating a request 48711 util.ScriptNodeDataSource._nPending++; 48712 var sUri = this.liveData + oRequest + this.generateRequestCallback(id); 48713 sUri = this.doBeforeGetScriptNode(sUri); 48714 YAHOO.log("DataSource is querying URL " + sUri, "info", this.toString()); 48715 this.getUtility.script(sUri, 48716 {autopurge: true, 48717 onsuccess: util.ScriptNodeDataSource._bumpPendingDown, 48718 onfail: util.ScriptNodeDataSource._bumpPendingDown}); 48719 48720 return tId; 48721} 48722 48723}); 48724 48725// Copy static members to ScriptNodeDataSource class 48726lang.augmentObject(util.ScriptNodeDataSource, DS); 48727 48728// Copy static members to ScriptNodeDataSource class 48729lang.augmentObject(util.ScriptNodeDataSource, { 48730 48731///////////////////////////////////////////////////////////////////////////// 48732// 48733// ScriptNodeDataSource private static properties 48734// 48735///////////////////////////////////////////////////////////////////////////// 48736 48737/** 48738 * Unique ID to track requests. 48739 * 48740 * @property _nId 48741 * @type Number 48742 * @private
48743 * @static 48744 */ 48745_nId : 0, 48746 48747/** 48748 * Counter for pending requests. When this is 0, it is safe to purge callbacks 48749 * array. 48750 * 48751 * @property _nPending 48752 * @type Number 48753 * @private 48754 * @static 48755 */ 48756_nPending : 0, 48757 48758/** 48759 * Global array of callback functions, one for each request sent. 48760 * 48761 * @property callbacks 48762 * @type Function[] 48763 * @static 48764 */ 48765callbacks : [] 48766 48767}); 48768 48769 48770 48771 48772 48773 48774 48775 48776 48777 48778 48779 48780 48781 48782/****************************************************************************/ 48783/****************************************************************************/ 48784/****************************************************************************/ 48785 48786/** 48787 * XHRDataSource class for accessing remote data via the YUI Connection Manager 48788 * Utility 48789 * 48790 * @namespace YAHOO.util 48791 * @class YAHOO.util.XHRDataSource 48792 * @extends YAHOO.util.DataSourceBase 48793 * @constructor 48794 * @param oLiveData {HTMLElement} Pointer to live data. 48795 * @param oConfigs {object} (optional) Object literal of configuration values. 48796 */ 48797util.XHRDataSource = function(oLiveData, oConfigs) { 48798 this.dataType = DS.TYPE_XHR; 48799 this.connMgr = this.connMgr || util.Connect; 48800 oLiveData = oLiveData || ""; 48801 48802 util.XHRDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 48803}; 48804 48805// XHRDataSource extends DataSourceBase 48806lang.extend(util.XHRDataSource, DS, { 48807 48808///////////////////////////////////////////////////////////////////////////// 48809// 48810// XHRDataSource public properties 48811// 48812///////////////////////////////////////////////////////////////////////////// 48813 48814 /** 48815 * Alias to YUI Connection Manager, to allow implementers to use a custom class. 48816 * 48817 * @property connMgr 48818 * @type Object 48819 * @default YAHOO.util.Connect 48820 */ 48821connMgr: null, 48822 48823 /** 48824 * Defines request/response management in the following manner: 48825 * <dl> 48826 * <dt>queueRequests</dt> 48827 * <dd>If a request is already in progress, wait until response is returned 48828 * before sending the next request.</dd> 48829 * 48830 * <dt>cancelStaleRequests</dt> 48831 * <dd>If a request is already in progress, cancel it before sending the next 48832 * request.</dd> 48833 * 48834 * <dt>ignoreStaleResponses</dt> 48835 * <dd>Send all requests, but handle only the response for the most recently 48836 * sent request.</dd> 48837 * 48838 * <dt>allowAll</dt> 48839 * <dd>Send all requests and handle all responses.</dd> 48840 * 48841 * </dl> 48842 * 48843 * @property connXhrMode 48844 * @type String 48845 * @default "allowAll" 48846 */ 48847connXhrMode: "allowAll", 48848 48849 /** 48850 * True if data is to be sent via POST. By default, data will be sent via GET. 48851 * 48852 * @property connMethodPost 48853 * @type Boolean 48854 * @default false 48855 */ 48856connMethodPost: false, 48857 48858 /** 48859 * The connection timeout defines how many milliseconds the XHR connection will 48860 * wait for a server response. Any non-zero value will enable the Connection Manager's 48861 * Auto-Abort feature. 48862 * 48863 * @property connTimeout 48864 * @type Number 48865 * @default 0 48866 */ 48867connTimeout: 0, 48868 48869///////////////////////////////////////////////////////////////////////////// 48870// 48871// XHRDataSource public methods 48872// 48873///////////////////////////////////////////////////////////////////////////// 48874 48875/** 48876 * Overriding method passes query to Connection Manager. The returned 48877 * response is then forwarded to the handleResponse function. 48878 * 48879 * @method makeConnection 48880 * @param oRequest {Object} Request object. 48881 * @param oCallback {Object} Callback object literal. 48882 * @param oCaller {Object} (deprecated) Use oCallback.scope. 48883 * @return {Number} Transaction ID. 48884 */ 48885makeConnection : function(oRequest, oCallback, oCaller) { 48886 48887 var oRawResponse = null; 48888 var tId = DS._nTransactionId++; 48889 this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller}); 48890 48891 // Set up the callback object and 48892 // pass the request in as a URL query and 48893 // forward the response to the handler 48894 var oSelf = this; 48895 var oConnMgr = this.connMgr; 48896 var oQueue = this._oQueue; 48897 48898 /** 48899 * Define Connection Manager success handler 48900 * 48901 * @method _xhrSuccess 48902 * @param oResponse {Object} HTTPXMLRequest object 48903 * @private 48904 */ 48905 var _xhrSuccess = function(oResponse) { 48906 // If response ID does not match last made request ID, 48907 // silently fail and wait for the next response 48908 if(oResponse && (this.connXhrMode == "ignoreStaleResponses") && 48909 (oResponse.tId != oQueue.conn.tId)) { 48910 YAHOO.log("Ignored stale response", "warn", this.toString()); 48911 return null; 48912 } 48913 // Error if no response 48914 else if(!oResponse) { 48915 this.fireEvent("dataErrorEvent", {request:oRequest, response:null, 48916 callback:oCallback, caller:oCaller, 48917 message:DS.ERROR_DATANULL}); 48918 YAHOO.log(DS.ERROR_DATANULL, "error", this.toString()); 48919 48920 // Send error response back to the caller with the error flag on 48921 DS.issueCallback(oCallback,[oRequest, {error:true}], true, oCaller); 48922 48923 return null; 48924 } 48925 // Forward to handler 48926 else { 48927 // Try to sniff data type if it has not been defined 48928 if(this.responseType === DS.TYPE_UNKNOWN) { 48929 var ctype = (oResponse.getResponseHeader) ? oResponse.getResponseHeader["Content-Type"] : null; 48930 if(ctype) { 48931 // xml 48932 if(ctype.indexOf("text/xml") > -1) { 48933 this.responseType = DS.TYPE_XML; 48934 } 48935 else if(ctype.indexOf("application/json") > -1) { // json 48936 this.responseType = DS.TYPE_JSON; 48937 } 48938 else if(ctype.indexOf("text/plain") > -1) { // text 48939 this.responseType = DS.TYPE_TEXT; 48940 } 48941 } 48942 } 48943 this.handleResponse(oRequest, oResponse, oCallback, oCaller, tId); 48944 } 48945 }; 48946 48947 /**
48948 * Define Connection Manager failure handler 48949 * 48950 * @method _xhrFailure 48951 * @param oResponse {Object} HTTPXMLRequest object 48952 * @private 48953 */ 48954 var _xhrFailure = function(oResponse) { 48955 this.fireEvent("dataErrorEvent", {request:oRequest, response: oResponse, 48956 callback:oCallback, caller:oCaller, 48957 message:DS.ERROR_DATAINVALID}); 48958 YAHOO.log(DS.ERROR_DATAINVALID + ": " + 48959 oResponse.statusText, "error", this.toString()); 48960 48961 // Backward compatibility 48962 if(lang.isString(this.liveData) && lang.isString(oRequest) && 48963 (this.liveData.lastIndexOf("?") !== this.liveData.length-1) && 48964 (oRequest.indexOf("?") !== 0)){ 48965 YAHOO.log("DataSources using XHR no longer automatically supply " + 48966 "a \"?\" between the host and query parameters" + 48967 " -- please check that the request URL is correct", "warn", this.toString()); 48968 } 48969 48970 // Send failure response back to the caller with the error flag on 48971 oResponse = oResponse || {}; 48972 oResponse.error = true; 48973 DS.issueCallback(oCallback,[oRequest,oResponse],true, oCaller); 48974 48975 return null; 48976 }; 48977 48978 /** 48979 * Define Connection Manager callback object 48980 * 48981 * @property _xhrCallback 48982 * @param oResponse {Object} HTTPXMLRequest object 48983 * @private 48984 */ 48985 var _xhrCallback = { 48986 success:_xhrSuccess, 48987 failure:_xhrFailure, 48988 scope: this 48989 }; 48990 48991 // Apply Connection Manager timeout 48992 if(lang.isNumber(this.connTimeout)) { 48993 _xhrCallback.timeout = this.connTimeout; 48994 } 48995 48996 // Cancel stale requests 48997 if(this.connXhrMode == "cancelStaleRequests") { 48998 // Look in queue for stale requests 48999 if(oQueue.conn) { 49000 if(oConnMgr.abort) { 49001 oConnMgr.abort(oQueue.conn); 49002 oQueue.conn = null; 49003 YAHOO.log("Canceled stale request", "warn", this.toString()); 49004 } 49005 else { 49006 YAHOO.log("Could not find Connection Manager abort() function", "error", this.toString()); 49007 } 49008 } 49009 } 49010 49011 // Get ready to send the request URL 49012 if(oConnMgr && oConnMgr.asyncRequest) { 49013 var sLiveData = this.liveData; 49014 var isPost = this.connMethodPost; 49015 var sMethod = (isPost) ? "POST" : "GET"; 49016 // Validate request 49017 var sUri = (isPost || !lang.isValue(oRequest)) ? sLiveData : sLiveData+oRequest; 49018 var sRequest = (isPost) ? oRequest : null; 49019 49020 // Send the request right away 49021 if(this.connXhrMode != "queueRequests") { 49022 oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest); 49023 } 49024 // Queue up then send the request 49025 else { 49026 // Found a request already in progress 49027 if(oQueue.conn) { 49028 var allRequests = oQueue.requests; 49029 // Add request to queue 49030 allRequests.push({request:oRequest, callback:_xhrCallback}); 49031 49032 // Interval needs to be started 49033 if(!oQueue.interval) { 49034 oQueue.interval = setInterval(function() { 49035 // Connection is in progress 49036 if(oConnMgr.isCallInProgress(oQueue.conn)) { 49037 return; 49038 } 49039 else { 49040 // Send next request 49041 if(allRequests.length > 0) { 49042 // Validate request 49043 sUri = (isPost || !lang.isValue(allRequests[0].request)) ? sLiveData : sLiveData+allRequests[0].request; 49044 sRequest = (isPost) ? allRequests[0].request : null; 49045 oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, allRequests[0].callback, sRequest); 49046 49047 // Remove request from queue 49048 allRequests.shift(); 49049 } 49050 // No more requests 49051 else { 49052 clearInterval(oQueue.interval); 49053 oQueue.interval = null; 49054 } 49055 } 49056 }, 50); 49057 } 49058 } 49059 // Nothing is in progress 49060 else { 49061 oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest); 49062 } 49063 } 49064 } 49065 else { 49066 YAHOO.log("Could not find Connection Manager asyncRequest() function", "error", this.toString()); 49067 // Send null response back to the caller with the error flag on 49068 DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller); 49069 } 49070 49071 return tId; 49072} 49073 49074}); 49075 49076// Copy static members to XHRDataSource class 49077lang.augmentObject(util.XHRDataSource, DS); 49078 49079 49080 49081 49082 49083 49084 49085 49086 49087 49088 49089 49090 49091/****************************************************************************/ 49092/****************************************************************************/ 49093/****************************************************************************/ 49094 49095/** 49096 * Factory class for creating a BaseDataSource subclass instance. The sublcass is 49097 * determined by oLiveData's type, unless the dataType config is explicitly passed in. 49098 * 49099 * @namespace YAHOO.util 49100 * @class YAHOO.util.DataSource 49101 * @constructor 49102 * @param oLiveData {HTMLElement} Pointer to live data. 49103 * @param oConfigs {object} (optional) Object literal of configuration values. 49104 */ 49105util.DataSource = function(oLiveData, oConfigs) { 49106 oConfigs = oConfigs || {}; 49107 49108 // Point to one of the subclasses, first by dataType if given, then by sniffing oLiveData type. 49109 var dataType = oConfigs.dataType; 49110 if(dataType) { 49111 if(dataType == DS.TYPE_LOCAL) { 49112 return new util.LocalDataSource(oLiveData, oConfigs); 49113 } 49114 else if(dataType == DS.TYPE_XHR) { 49115 return new util.XHRDataSource(oLiveData, oConfigs); 49116 } 49117 else if(dataType == DS.TYPE_SCRIPTNODE) { 49118 return new util.ScriptNodeDataSource(oLiveData, oConfigs); 49119 } 49120 else if(dataType == DS.TYPE_JSFUNCTION) { 49121 return new util.FunctionDataSource(oLiveData, oConfigs); 49122 } 49123 } 49124 49125 if(YAHOO.lang.isString(oLiveData)) { // strings default to xhr 49126 return new util.XHRDataSource(oLiveData, oConfigs); 49127 } 49128 else if(YAHOO.lang.isFunction(oLiveData)) { 49129 return new util.FunctionDataSource(oLiveData, oConfigs); 49130 } 49131 else { // ultimate default is local 49132 return new util.LocalDataSource(oLiveData, oConfigs); 49133 } 49134}; 49135 49136// Copy static members to DataSource class 49137lang.augmentObject(util.DataSource, DS); 49138 49139})(); 49140 49141/****************************************************************************/ 49142/****************************************************************************/ 49143/****************************************************************************/ 49144 49145/** 49146 * The static Number class provides helper functions to deal with data of type 49147 * Number. 49148 * 49149 * @namespace YAHOO.util 49150 * @requires yahoo 49151 * @class Number 49152 * @static 49153 */ 49154 YAHOO.util.Number = { 49155 49156 /** 49157 * Takes a native JavaScript Number and formats to a string for display. 49158 * 49159 * @method format 49160 * @param nData {Number} Number. 49161 * @param oConfig {Object} (Optional) Optional configuration values: 49162 * <dl> 49163 * <dt>format</dt>
49164 * <dd>String used as a template for formatting positive numbers. 49165 * {placeholders} in the string are applied from the values in this 49166 * config object. {number} is used to indicate where the numeric portion 49167 * of the output goes. For example "{prefix}{number} per item" 49168 * might yield "$5.25 per item". The only required 49169 * {placeholder} is {number}.</dd> 49170 * 49171 * <dt>negativeFormat</dt> 49172 * <dd>Like format, but applied to negative numbers. If set to null, 49173 * defaults from the configured format, prefixed with -. This is 49174 * separate from format to support formats like "($12,345.67)". 49175 * 49176 * <dt>prefix {String} (deprecated, use format/negativeFormat)</dt> 49177 * <dd>String prepended before each number, like a currency designator "$"</dd> 49178 * <dt>decimalPlaces {Number}</dt> 49179 * <dd>Number of decimal places to round.</dd> 49180 * 49181 * <dt>decimalSeparator {String}</dt> 49182 * <dd>Decimal separator</dd> 49183 * 49184 * <dt>thousandsSeparator {String}</dt> 49185 * <dd>Thousands separator</dd> 49186 * 49187 * <dt>suffix {String} (deprecated, use format/negativeFormat)</dt> 49188 * <dd>String appended after each number, like " items" (note the space)</dd> 49189 * </dl> 49190 * @return {String} Formatted number for display. Note, the following values 49191 * return as "": null, undefined, NaN, "". 49192 */ 49193 format : function(n, cfg) { 49194 if (n === '' || n === null || !isFinite(n)) { 49195 return ''; 49196 } 49197 49198 n = +n; 49199 cfg = YAHOO.lang.merge(YAHOO.util.Number.format.defaults, (cfg || {})); 49200 49201 var stringN = n+'', 49202 absN = Math.abs(n), 49203 places = cfg.decimalPlaces || 0, 49204 sep = cfg.thousandsSeparator, 49205 negFmt = cfg.negativeFormat || ('-' + cfg.format), 49206 s, bits, i, precision; 49207 49208 if (negFmt.indexOf('#') > -1) { 49209 // for backward compatibility of negativeFormat supporting '-#' 49210 negFmt = negFmt.replace(/#/, cfg.format); 49211 } 49212 49213 if (places < 0) { 49214 // Get rid of the decimal info 49215 s = absN - (absN % 1) + ''; 49216 i = s.length + places; 49217 49218 // avoid 123 vs decimalPlaces -4 (should return "0") 49219 if (i > 0) { 49220 // leverage toFixed by making 123 => 0.123 for the rounding 49221 // operation, then add the appropriate number of zeros back on 49222 s = Number('.' + s).toFixed(i).slice(2) + 49223 new Array(s.length - i + 1).join('0'); 49224 } else { 49225 s = "0"; 49226 } 49227 } else { 49228 // Avoid toFixed on floats: 49229 // Bug 2528976 49230 // Bug 2528977 49231 var unfloatedN = absN+''; 49232 if(places > 0 || unfloatedN.indexOf('.') > 0) { 49233 var power = Math.pow(10, places); 49234 s = Math.round(absN * power) / power + ''; 49235 var dot = s.indexOf('.'), 49236 padding, zeroes; 49237 49238 // Add padding 49239 if(dot < 0) { 49240 padding = places; 49241 zeroes = (Math.pow(10, padding) + '').substring(1); 49242 if(places > 0) { 49243 s = s + '.' + zeroes; 49244 } 49245 } 49246 else { 49247 padding = places - (s.length - dot - 1); 49248 zeroes = (Math.pow(10, padding) + '').substring(1); 49249 s = s + zeroes; 49250 } 49251 } 49252 else { 49253 s = absN.toFixed(places)+''; 49254 } 49255 } 49256 49257 bits = s.split(/\D/); 49258 49259 if (absN >= 1000) { 49260 i = bits[0].length % 3 || 3; 49261 49262 bits[0] = bits[0].slice(0,i) + 49263 bits[0].slice(i).replace(/(\d{3})/g, sep + '$1'); 49264 49265 } 49266 49267 return YAHOO.util.Number.format._applyFormat( 49268 (n < 0 ? negFmt : cfg.format), 49269 bits.join(cfg.decimalSeparator), 49270 cfg); 49271 } 49272}; 49273 49274/** 49275 * <p>Default values for Number.format behavior. Override properties of this 49276 * object if you want every call to Number.format in your system to use 49277 * specific presets.</p> 49278 * 49279 * <p>Available keys include:</p> 49280 * <ul> 49281 * <li>format</li> 49282 * <li>negativeFormat</li> 49283 * <li>decimalSeparator</li> 49284 * <li>decimalPlaces</li> 49285 * <li>thousandsSeparator</li> 49286 * <li>prefix/suffix or any other token you want to use in the format templates</li> 49287 * </ul> 49288 * 49289 * @property Number.format.defaults 49290 * @type {Object} 49291 * @static 49292 */ 49293YAHOO.util.Number.format.defaults = { 49294 format : '{prefix}{number}{suffix}', 49295 negativeFormat : null, // defaults to -(format) 49296 decimalSeparator : '.', 49297 decimalPlaces : null, 49298 thousandsSeparator : '' 49299}; 49300 49301/** 49302 * Apply any special formatting to the "d,ddd.dd" string. Takes either the 49303 * cfg.format or cfg.negativeFormat template and replaces any {placeholders} 49304 * with either the number or a value from a so-named property of the config 49305 * object. 49306 * 49307 * @method Number.format._applyFormat
49308 * @static 49309 * @param tmpl {String} the cfg.format or cfg.numberFormat template string 49310 * @param num {String} the number with separators and decimalPlaces applied 49311 * @param data {Object} the config object, used here to populate {placeholder}s 49312 * @return {String} the number with any decorators added 49313 */ 49314YAHOO.util.Number.format._applyFormat = function (tmpl, num, data) { 49315 return tmpl.replace(/\{(\w+)\}/g, function (_, token) { 49316 return token === 'number' ? num : 49317 token in data ? data[token] : ''; 49318 }); 49319}; 49320 49321 49322/****************************************************************************/ 49323/****************************************************************************/ 49324/****************************************************************************/ 49325 49326(function () { 49327 49328var xPad=function (x, pad, r) 49329{ 49330 if(typeof r === 'undefined') 49331 { 49332 r=10; 49333 } 49334 for( ; parseInt(x, 10)<r && r>1; r/=10) { 49335 x = pad.toString() + x; 49336 } 49337 return x.toString(); 49338}; 49339 49340 49341/** 49342 * The static Date class provides helper functions to deal with data of type Date. 49343 * 49344 * @namespace YAHOO.util 49345 * @requires yahoo 49346 * @class Date 49347 * @static 49348 */ 49349 var Dt = { 49350 formats: { 49351 a: function (d, l) { return l.a[d.getDay()]; }, 49352 A: function (d, l) { return l.A[d.getDay()]; }, 49353 b: function (d, l) { return l.b[d.getMonth()]; }, 49354 B: function (d, l) { return l.B[d.getMonth()]; }, 49355 C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); }, 49356 d: ['getDate', '0'], 49357 e: ['getDate', ' '], 49358 g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); }, 49359 G: function (d) { 49360 var y = d.getFullYear(); 49361 var V = parseInt(Dt.formats.V(d), 10); 49362 var W = parseInt(Dt.formats.W(d), 10); 49363 49364 if(W > V) { 49365 y++; 49366 } else if(W===0 && V>=52) { 49367 y--; 49368 } 49369 49370 return y; 49371 }, 49372 H: ['getHours', '0'], 49373 I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); }, 49374 j: function (d) { 49375 var gmd_1 = new Date('' + d.getFullYear() + '/1/1 GMT'); 49376 var gmdate = new Date('' + d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate() + ' GMT'); 49377 var ms = gmdate - gmd_1; 49378 var doy = parseInt(ms/60000/60/24, 10)+1; 49379 return xPad(doy, 0, 100); 49380 }, 49381 k: ['getHours', ' '], 49382 l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, ' '); }, 49383 m: function (d) { return xPad(d.getMonth()+1, 0); }, 49384 M: ['getMinutes', '0'], 49385 p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; }, 49386 P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; }, 49387 s: function (d, l) { return parseInt(d.getTime()/1000, 10); }, 49388 S: ['getSeconds', '0'], 49389 u: function (d) { var dow = d.getDay(); return dow===0?7:dow; }, 49390 U: function (d) { 49391 var doy = parseInt(Dt.formats.j(d), 10); 49392 var rdow = 6-d.getDay(); 49393 var woy = parseInt((doy+rdow)/7, 10); 49394 return xPad(woy, 0); 49395 }, 49396 V: function (d) { 49397 var woy = parseInt(Dt.formats.W(d), 10); 49398 var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); 49399 // First week is 01 and not 00 as in the case of %U and %W, 49400 // so we add 1 to the final result except if day 1 of the year 49401 // is a Monday (then %W returns 01). 49402 // We also need to subtract 1 if the day 1 of the year is 49403 // Friday-Sunday, so the resulting equation becomes: 49404 var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); 49405 if(idow === 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) 49406 { 49407 idow = 1; 49408 } 49409 else if(idow === 0) 49410 { 49411 idow = Dt.formats.V(new Date('' + (d.getFullYear()-1) + '/12/31')); 49412 } 49413 49414 return xPad(idow, 0); 49415 }, 49416 w: 'getDay', 49417 W: function (d) { 49418 var doy = parseInt(Dt.formats.j(d), 10); 49419 var rdow = 7-Dt.formats.u(d); 49420 var woy = parseInt((doy+rdow)/7, 10); 49421 return xPad(woy, 0, 10); 49422 }, 49423 y: function (d) { return xPad(d.getFullYear()%100, 0); }, 49424 Y: 'getFullYear', 49425 z: function (d) { 49426 var o = d.getTimezoneOffset(); 49427 var H = xPad(parseInt(Math.abs(o/60), 10), 0); 49428 var M = xPad(Math.abs(o%60), 0); 49429 return (o>0?'-':'+') + H + M; 49430 }, 49431 Z: function (d) { 49432 var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, '$2').replace(/[a-z ]/g, ''); 49433 if(tz.length > 4) { 49434 tz = Dt.formats.z(d); 49435 } 49436 return tz; 49437 }, 49438 '%': function (d) { return '%'; } 49439 }, 49440 49441 aggregates: { 49442 c: 'locale', 49443 D: '%m/%d/%y', 49444 F: '%Y-%m-%d', 49445 h: '%b', 49446 n: '\n', 49447 r: 'locale', 49448 R: '%H:%M', 49449 t: '\t', 49450 T: '%H:%M:%S', 49451 x: 'locale', 49452 X: 'locale' 49453 //'+': '%a %b %e %T %Z %Y' 49454 }, 49455 49456 /** 49457 * Takes a native JavaScript Date and formats to string for display to user. 49458 * 49459 * @method format 49460 * @param oDate {Date} Date. 49461 * @param oConfig {Object} (Optional) Object literal of configuration values: 49462 * <dl> 49463 * <dt>format <String></dt>
49464 * <dd> 49465 * <p> 49466 * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at 49467 * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a> 49468 * </p> 49469 * <p> 49470 * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a> 49471 * </p> 49472 * <p> 49473 * This javascript implementation supports all the PHP specifiers and a few more. The full list is below: 49474 * </p> 49475 * <dl> 49476 * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd> 49477 * <dt>%A</dt> <dd>full weekday name according to the current locale</dd> 49478 * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd> 49479 * <dt>%B</dt> <dd>full month name according to the current locale</dd> 49480 * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd> 49481 * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd> 49482 * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd> 49483 * <dt>%D</dt> <dd>same as %m/%d/%y</dd> 49484 * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31')</dd> 49485 * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd> 49486 * <dt>%g</dt> <dd>like %G, but without the century</dd> 49487 * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd> 49488 * <dt>%h</dt> <dd>same as %b</dd> 49489 * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd> 49490 * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd> 49491 * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd> 49492 * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd> 49493 * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd> 49494 * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd> 49495 * <dt>%M</dt> <dd>minute as a decimal number</dd> 49496 * <dt>%n</dt> <dd>newline character</dd> 49497 * <dt>%p</dt> <dd>either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale</dd> 49498 * <dt>%P</dt> <dd>like %p, but lower case</dd> 49499 * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd> 49500 * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd> 49501 * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd> 49502 * <dt>%S</dt> <dd>second as a decimal number</dd> 49503 * <dt>%t</dt> <dd>tab character</dd> 49504 * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd> 49505 * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd> 49506 * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the 49507 * first Sunday as the first day of the first week</dd> 49508 * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number, 49509 * range 01 to 53, where week 1 is the first week that has at least 4 days 49510 * in the current year, and with Monday as the first day of the week.</dd> 49511 * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd> 49512 * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the 49513 * first Monday as the first day of the first week</dd> 49514 * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd> 49515 * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd> 49516 * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd> 49517 * <dt>%Y</dt> <dd>year as a decimal number including the century</dd> 49518 * <dt>%z</dt> <dd>numerical time zone representation</dd> 49519 * <dt>%Z</dt> <dd>time zone name or abbreviation</dd> 49520 * <dt>%%</dt> <dd>a literal `%' character</dd> 49521 * </dl> 49522 * </dd> 49523 * </dl> 49524 * @param sLocale {String} (Optional) The locale to use when displaying days of week, 49525 * months of the year, and other locale specific strings. The following locales are 49526 * built in: 49527 * <dl> 49528 * <dt>en</dt>
49529 * <dd>English</dd> 49530 * <dt>en-US</dt> 49531 * <dd>US English</dd> 49532 * <dt>en-GB</dt> 49533 * <dd>British English</dd> 49534 * <dt>en-AU</dt> 49535 * <dd>Australian English (identical to British English)</dd> 49536 * </dl> 49537 * More locales may be added by subclassing of YAHOO.util.DateLocale. 49538 * See YAHOO.util.DateLocale for more information. 49539 * @return {HTML} Formatted date for display. Non-date values are passed 49540 * through as-is. 49541 * @sa YAHOO.util.DateLocale 49542 */ 49543 format : function (oDate, oConfig, sLocale) { 49544 oConfig = oConfig || {}; 49545 49546 if(!(oDate instanceof Date)) { 49547 return YAHOO.lang.isValue(oDate) ? oDate : ""; 49548 } 49549 49550 var format = oConfig.format || "%m/%d/%Y"; 49551 49552 // Be backwards compatible, support strings that are 49553 // exactly equal to YYYY/MM/DD, DD/MM/YYYY and MM/DD/YYYY 49554 if(format === 'YYYY/MM/DD') { 49555 format = '%Y/%m/%d'; 49556 } else if(format === 'DD/MM/YYYY') { 49557 format = '%d/%m/%Y'; 49558 } else if(format === 'MM/DD/YYYY') { 49559 format = '%m/%d/%Y'; 49560 } 49561 // end backwards compatibility block 49562 49563 sLocale = sLocale || "en"; 49564 49565 // Make sure we have a definition for the requested locale, or default to en. 49566 if(!(sLocale in YAHOO.util.DateLocale)) { 49567 if(sLocale.replace(/-[a-zA-Z]+$/, '') in YAHOO.util.DateLocale) { 49568 sLocale = sLocale.replace(/-[a-zA-Z]+$/, ''); 49569 } else { 49570 sLocale = "en"; 49571 } 49572 } 49573 49574 var aLocale = YAHOO.util.DateLocale[sLocale]; 49575 49576 var replace_aggs = function (m0, m1) { 49577 var f = Dt.aggregates[m1]; 49578 return (f === 'locale' ? aLocale[m1] : f); 49579 }; 49580 49581 var replace_formats = function (m0, m1) { 49582 var f = Dt.formats[m1]; 49583 if(typeof f === 'string') { // string => built in date function 49584 return oDate[f](); 49585 } else if(typeof f === 'function') { // function => our own function 49586 return f.call(oDate, oDate, aLocale); 49587 } else if(typeof f === 'object' && typeof f[0] === 'string') { // built in function with padding 49588 return xPad(oDate[f[0]](), f[1]); 49589 } else { 49590 return m1; 49591 } 49592 }; 49593 49594 // First replace aggregates (run in a loop because an agg may be made up of other aggs) 49595 while(format.match(/%[cDFhnrRtTxX]/)) { 49596 format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs); 49597 } 49598 49599 // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a) 49600 var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats); 49601 49602 replace_aggs = replace_formats = undefined; 49603 49604 return str; 49605 } 49606 }; 49607 49608 YAHOO.namespace("YAHOO.util"); 49609 YAHOO.util.Date = Dt; 49610 49611/** 49612 * The DateLocale class is a container and base class for all 49613 * localised date strings used by YAHOO.util.Date. It is used 49614 * internally, but may be extended to provide new date localisations. 49615 * 49616 * To create your own DateLocale, follow these steps: 49617 * <ol> 49618 * <li>Find an existing locale that matches closely with your needs</li> 49619 * <li>Use this as your base class. Use YAHOO.util.DateLocale if nothing 49620 * matches.</li> 49621 * <li>Create your own class as an extension of the base class using 49622 * YAHOO.lang.merge, and add your own localisations where needed.</li> 49623 * </ol> 49624 * See the YAHOO.util.DateLocale['en-US'] and YAHOO.util.DateLocale['en-GB'] 49625 * classes which extend YAHOO.util.DateLocale['en']. 49626 * 49627 * For example, to implement locales for French french and Canadian french, 49628 * we would do the following: 49629 * <ol> 49630 * <li>For French french, we have no existing similar locale, so use 49631 * YAHOO.util.DateLocale as the base, and extend it: 49632 * <pre> 49633 * YAHOO.util.DateLocale['fr'] = YAHOO.lang.merge(YAHOO.util.DateLocale, { 49634 * a: ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'], 49635 * A: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], 49636 * b: ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'], 49637 * B: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], 49638 * c: '%a %d %b %Y %T %Z', 49639 * p: ['', ''], 49640 * P: ['', ''], 49641 * x: '%d.%m.%Y', 49642 * X: '%T' 49643 * }); 49644 * </pre> 49645 * </li> 49646 * <li>For Canadian french, we start with French french and change the meaning of \%x: 49647 * <pre> 49648 * YAHOO.util.DateLocale['fr-CA'] = YAHOO.lang.merge(YAHOO.util.DateLocale['fr'], { 49649 * x: '%Y-%m-%d' 49650 * }); 49651 * </pre> 49652 * </li> 49653 * </ol> 49654 * 49655 * With that, you can use your new locales: 49656 * <pre> 49657 * var d = new Date("2008/04/22"); 49658 * YAHOO.util.Date.format(d, {format: "%A, %d %B == %x"}, "fr"); 49659 * </pre> 49660 * will return: 49661 * <pre> 49662 * mardi, 22 avril == 22.04.2008 49663 * </pre> 49664 * And 49665 * <pre> 49666 * YAHOO.util.Date.format(d, {format: "%A, %d %B == %x"}, "fr-CA"); 49667 * </pre> 49668 * Will return: 49669 * <pre> 49670 * mardi, 22 avril == 2008-04-22 49671 * </pre> 49672 * @namespace YAHOO.util 49673 * @requires yahoo 49674 * @class DateLocale 49675 */ 49676 YAHOO.util.DateLocale = { 49677 a: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 49678 A: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 49679 b: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 49680 B: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 49681 c: '%a %d %b %Y %T %Z', 49682 p: ['AM', 'PM'], 49683 P: ['am', 'pm'], 49684 r: '%I:%M:%S %p', 49685 x: '%d/%m/%y', 49686 X: '%T' 49687 }; 49688 49689 YAHOO.util.DateLocale['en'] = YAHOO.lang.merge(YAHOO.util.DateLocale, {}); 49690 49691 YAHOO.util.DateLocale['en-US'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en'], {
49692 c: '%a %d %b %Y %I:%M:%S %p %Z', 49693 x: '%m/%d/%Y', 49694 X: '%I:%M:%S %p' 49695 }); 49696 49697 YAHOO.util.DateLocale['en-GB'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en'], { 49698 r: '%l:%M:%S %P %Z' 49699 }); 49700 YAHOO.util.DateLocale['en-AU'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en']); 49701 49702})(); 49703 49704YAHOO.register("datasource", YAHOO.util.DataSource, {version: "2.9.0", build: "2800"}); 49705/* 49706Copyright (c) 2011, Yahoo! Inc. All rights reserved. 49707Code licensed under the BSD License: 49708http://developer.yahoo.com/yui/license.html 49709version: 2.9.0 49710*/ 49711///////////////////////////////////////////////////////////////////////////// 49712// 49713// YAHOO.widget.DataSource Backwards Compatibility 49714// 49715///////////////////////////////////////////////////////////////////////////// 49716 49717YAHOO.widget.DS_JSArray = YAHOO.util.LocalDataSource; 49718 49719YAHOO.widget.DS_JSFunction = YAHOO.util.FunctionDataSource; 49720 49721YAHOO.widget.DS_XHR = function(sScriptURI, aSchema, oConfigs) { 49722 var DS = new YAHOO.util.XHRDataSource(sScriptURI, oConfigs); 49723 DS._aDeprecatedSchema = aSchema; 49724 return DS; 49725}; 49726 49727YAHOO.widget.DS_ScriptNode = function(sScriptURI, aSchema, oConfigs) { 49728 var DS = new YAHOO.util.ScriptNodeDataSource(sScriptURI, oConfigs); 49729 DS._aDeprecatedSchema = aSchema; 49730 return DS; 49731}; 49732 49733YAHOO.widget.DS_XHR.TYPE_JSON = YAHOO.util.DataSourceBase.TYPE_JSON; 49734YAHOO.widget.DS_XHR.TYPE_XML = YAHOO.util.DataSourceBase.TYPE_XML; 49735YAHOO.widget.DS_XHR.TYPE_FLAT = YAHOO.util.DataSourceBase.TYPE_TEXT; 49736 49737// TODO: widget.DS_ScriptNode.scriptCallbackParam 49738 49739 49740 49741 /** 49742 * The AutoComplete control provides the front-end logic for text-entry suggestion and 49743 * completion functionality. 49744 * 49745 * @module autocomplete 49746 * @requires yahoo, dom, event, datasource 49747 * @optional animation 49748 * @namespace YAHOO.widget 49749 * @title AutoComplete Widget 49750 */ 49751 49752/****************************************************************************/ 49753/****************************************************************************/ 49754/****************************************************************************/ 49755 49756/** 49757 * The AutoComplete class provides the customizable functionality of a plug-and-play DHTML 49758 * auto completion widget. Some key features: 49759 * <ul> 49760 * <li>Navigate with up/down arrow keys and/or mouse to pick a selection</li> 49761 * <li>The drop down container can "roll down" or "fly out" via configurable 49762 * animation</li> 49763 * <li>UI look-and-feel customizable through CSS, including container 49764 * attributes, borders, position, fonts, etc</li> 49765 * </ul> 49766 * 49767 * @class AutoComplete 49768 * @constructor 49769 * @param elInput {HTMLElement} DOM element reference of an input field. 49770 * @param elInput {String} String ID of an input field. 49771 * @param elContainer {HTMLElement} DOM element reference of an existing DIV. 49772 * @param elContainer {String} String ID of an existing DIV. 49773 * @param oDataSource {YAHOO.widget.DataSource} DataSource instance. 49774 * @param oConfigs {Object} (optional) Object literal of configuration params. 49775 */ 49776YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) { 49777 if(elInput && elContainer && oDataSource) { 49778 // Validate DataSource 49779 if(oDataSource && YAHOO.lang.isFunction(oDataSource.sendRequest)) { 49780 this.dataSource = oDataSource; 49781 } 49782 else { 49783 YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString()); 49784 return; 49785 } 49786 49787 // YAHOO.widget.DataSource schema backwards compatibility 49788 // Converted deprecated schema into supported schema 49789 // First assume key data is held in position 0 of results array 49790 this.key = 0; 49791 var schema = oDataSource.responseSchema; 49792 // An old school schema has been defined in the deprecated DataSource constructor 49793 if(oDataSource._aDeprecatedSchema) { 49794 var aDeprecatedSchema = oDataSource._aDeprecatedSchema; 49795 if(YAHOO.lang.isArray(aDeprecatedSchema)) { 49796 49797 if((oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_JSON) || 49798 (oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_UNKNOWN)) { // Used to default to unknown 49799 // Store the resultsList 49800 schema.resultsList = aDeprecatedSchema[0]; 49801 // Store the key 49802 this.key = aDeprecatedSchema[1]; 49803 // Only resultsList and key are defined, so grab all the data 49804 schema.fields = (aDeprecatedSchema.length < 3) ? null : aDeprecatedSchema.slice(1); 49805 } 49806 else if(oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_XML) { 49807 schema.resultNode = aDeprecatedSchema[0]; 49808 this.key = aDeprecatedSchema[1]; 49809 schema.fields = aDeprecatedSchema.slice(1); 49810 } 49811 else if(oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_TEXT) { 49812 schema.recordDelim = aDeprecatedSchema[0]; 49813 schema.fieldDelim = aDeprecatedSchema[1]; 49814 } 49815 oDataSource.responseSchema = schema; 49816 } 49817 } 49818 49819 // Validate input element 49820 if(YAHOO.util.Dom.inDocument(elInput)) { 49821 if(YAHOO.lang.isString(elInput)) { 49822 this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput; 49823 this._elTextbox = document.getElementById(elInput); 49824 } 49825 else { 49826 this._sName = (elInput.id) ? 49827 "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id: 49828 "instance" + YAHOO.widget.AutoComplete._nIndex; 49829 this._elTextbox = elInput; 49830 } 49831 YAHOO.util.Dom.addClass(this._elTextbox, "yui-ac-input"); 49832 } 49833 else { 49834 YAHOO.log("Could not instantiate AutoComplete due to an invali
49834d input element", "error", this.toString()); 49835 return; 49836 } 49837 49838 // Validate container element 49839 if(YAHOO.util.Dom.inDocument(elContainer)) { 49840 if(YAHOO.lang.isString(elContainer)) { 49841 this._elContainer = document.getElementById(elContainer); 49842 } 49843 else { 49844 this._elContainer = elContainer; 49845 } 49846 if(this._elContainer.style.display == "none") { 49847 YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString()); 49848 } 49849 49850 // For skinning 49851 var elParent = this._elContainer.parentNode; 49852 var elTag = elParent.tagName.toLowerCase(); 49853 if(elTag == "div") { 49854 YAHOO.util.Dom.addClass(elParent, "yui-ac"); 49855 } 49856 else { 49857 YAHOO.log("Could not find the wrapper element for skinning", "warn", this.toString()); 49858 } 49859 } 49860 else { 49861 YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString()); 49862 return; 49863 } 49864 49865 // Default applyLocalFilter setting is to enable for local sources 49866 if(this.dataSource.dataType === YAHOO.util.DataSourceBase.TYPE_LOCAL) { 49867 this.applyLocalFilter = true; 49868 } 49869 49870 // Set any config params passed in to override defaults 49871 if(oConfigs && (oConfigs.constructor == Object)) { 49872 for(var sConfig in oConfigs) { 49873 if(sConfig) { 49874 this[sConfig] = oConfigs[sConfig]; 49875 } 49876 } 49877 } 49878 49879 // Initialization sequence 49880 this._initContainerEl(); 49881 this._initProps(); 49882 this._initListEl(); 49883 this._initContainerHelperEls(); 49884 49885 // Set up events 49886 var oSelf = this; 49887 var elTextbox = this._elTextbox; 49888 49889 // Dom events 49890 YAHOO.util.Event.addListener(elTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf); 49891 YAHOO.util.Event.addListener(elTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf); 49892 YAHOO.util.Event.addListener(elTextbox,"focus",oSelf._onTextboxFocus,oSelf); 49893 YAHOO.util.Event.addListener(elTextbox,"blur",oSelf._onTextboxBlur,oSelf); 49894 YAHOO.util.Event.addListener(elContainer,"mouseover",oSelf._onContainerMouseover,oSelf); 49895 YAHOO.util.Event.addListener(elContainer,"mouseout",oSelf._onContainerMouseout,oSelf); 49896 YAHOO.util.Event.addListener(elContainer,"click",oSelf._onContainerClick,oSelf); 49897 YAHOO.util.Event.addListener(elContainer,"scroll",oSelf._onContainerScroll,oSelf); 49898 YAHOO.util.Event.addListener(elContainer,"resize",oSelf._onContainerResize,oSelf); 49899 YAHOO.util.Event.addListener(elTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf); 49900 YAHOO.util.Event.addListener(window,"unload",oSelf._onWindowUnload,oSelf); 49901 49902 // Custom events 49903 this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this); 49904 this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this); 49905 this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this); 49906 this.dataRequestCancelEvent = new YAHOO.util.CustomEvent("dataRequestCancel", this); 49907 this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this); 49908 this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this); 49909 this.containerPopulateEvent = new YAHOO.util.CustomEvent("containerPopulate", this); 49910 this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this); 49911 this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this); 49912 this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this); 49913 this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this); 49914 this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this); 49915 this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this); 49916 this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this); 49917 this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this); 49918 this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this); 49919 this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this); 49920 this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this); 49921 this.textboxChangeEvent = new YAHOO.util.CustomEvent("textboxChange", this); 49922
49923 // Finish up 49924 elTextbox.setAttribute("autocomplete","off"); 49925 YAHOO.widget.AutoComplete._nIndex++; 49926 YAHOO.log("AutoComplete initialized","info",this.toString()); 49927 } 49928 // Required arguments were not found 49929 else { 49930 YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString()); 49931 } 49932}; 49933 49934///////////////////////////////////////////////////////////////////////////// 49935// 49936// Public member variables 49937// 49938///////////////////////////////////////////////////////////////////////////// 49939 49940/** 49941 * The DataSource object that encapsulates the data used for auto completion. 49942 * This object should be an inherited object from YAHOO.widget.DataSource. 49943 * 49944 * @property dataSource 49945 * @type YAHOO.widget.DataSource 49946 */ 49947YAHOO.widget.AutoComplete.prototype.dataSource = null; 49948 49949/** 49950 * By default, results from local DataSources will pass through the filterResults 49951 * method to apply a client-side matching algorithm. 49952 * 49953 * @property applyLocalFilter 49954 * @type Boolean 49955 * @default true for local arrays and json, otherwise false 49956 */ 49957YAHOO.widget.AutoComplete.prototype.applyLocalFilter = null; 49958 49959/** 49960 * When applyLocalFilter is true, the local filtering algorthim can have case sensitivity 49961 * enabled. 49962 * 49963 * @property queryMatchCase 49964 * @type Boolean 49965 * @default false 49966 */ 49967YAHOO.widget.AutoComplete.prototype.queryMatchCase = false; 49968 49969/** 49970 * When applyLocalFilter is true, results can be locally filtered to return 49971 * matching strings that "contain" the query string rather than simply "start with" 49972 * the query string. 49973 * 49974 * @property queryMatchContains 49975 * @type Boolean 49976 * @default false 49977 */ 49978YAHOO.widget.AutoComplete.prototype.queryMatchContains = false; 49979 49980/** 49981 * Enables query subset matching. When the DataSource's cache is enabled and queryMatchSubset is 49982 * true, substrings of queries will return matching cached results. For 49983 * instance, if the first query is for "abc" susequent queries that start with 49984 * "abc", like "abcd", will be queried against the cache, and not the live data 49985 * source. Recommended only for DataSources that return comprehensive results 49986 * for queries with very few characters. 49987 * 49988 * @property queryMatchSubset 49989 * @type Boolean 49990 * @default false 49991 * 49992 */ 49993YAHOO.widget.AutoComplete.prototype.queryMatchSubset = false; 49994 49995/** 49996 * Number of characters that must be entered before querying for results. A negative value 49997 * effectively turns off the widget. A value of 0 allows queries of null or empty string 49998 * values. 49999 * 50000 * @property minQueryLength 50001 * @type Number 50002 * @default 1 50003 */ 50004YAHOO.widget.AutoComplete.prototype.minQueryLength = 1; 50005 50006/** 50007 * Maximum number of results to display in results container. 50008 * 50009 * @property maxResultsDisplayed 50010 * @type Number 50011 * @default 10 50012 */ 50013YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10; 50014 50015/** 50016 * Number of seconds to delay before submitting a query request. If a query 50017 * request is received before a previous one has completed its delay, the 50018 * previous request is cancelled and the new request is set to the delay. If 50019 * typeAhead is also enabled, this value must always be less than the typeAheadDelay 50020 * in order to avoid certain race conditions. 50021 * 50022 * @property queryDelay 50023 * @type Number 50024 * @default 0.2 50025 */ 50026YAHOO.widget.AutoComplete.prototype.queryDelay = 0.2; 50027 50028/** 50029 * If typeAhead is true, number of seconds to delay before updating input with 50030 * typeAhead value. In order to prevent certain race conditions, this value must 50031 * always be greater than the queryDelay. 50032 * 50033 * @property typeAheadDelay 50034 * @type Number 50035 * @default 0.5 50036 */ 50037YAHOO.widget.AutoComplete.prototype.typeAheadDelay = 0.5; 50038 50039/** 50040 * When IME usage is detected or interval detection is explicitly enabled, 50041 * AutoComplete will detect the input value at the given interval and send a 50042 * query if the value has changed. 50043 * 50044 * @property queryInterval 50045 * @type Number 50046 * @default 500 50047 */ 50048YAHOO.widget.AutoComplete.prototype.queryInterval = 500; 50049 50050/** 50051 * Class name of a highlighted item within results container. 50052 * 50053 * @property highlightClassName 50054 * @type String 50055 * @default "yui-ac-highlight" 50056 */ 50057YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight"; 50058 50059/**
50060 * Class name of a pre-highlighted item within results container. 50061 * 50062 * @property prehighlightClassName 50063 * @type String 50064 */ 50065YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null; 50066 50067/** 50068 * Query delimiter. A single character separator for multiple delimited 50069 * selections. Multiple delimiter characteres may be defined as an array of 50070 * strings. A null value or empty string indicates that query results cannot 50071 * be delimited. This feature is not recommended if you need forceSelection to 50072 * be true. 50073 * 50074 * @property delimChar 50075 * @type String | String[] 50076 */ 50077YAHOO.widget.AutoComplete.prototype.delimChar = null; 50078 50079/** 50080 * Whether or not the first item in results container should be automatically highlighted 50081 * on expand. 50082 * 50083 * @property autoHighlight 50084 * @type Boolean 50085 * @default true 50086 */ 50087YAHOO.widget.AutoComplete.prototype.autoHighlight = true; 50088 50089/** 50090 * If autohighlight is enabled, whether or not the input field should be automatically updated 50091 * with the first query result as the user types, auto-selecting the substring portion 50092 * of the first result that the user has not yet typed. 50093 * 50094 * @property typeAhead 50095 * @type Boolean 50096 * @default false 50097 */ 50098YAHOO.widget.AutoComplete.prototype.typeAhead = false; 50099 50100/** 50101 * Whether or not to animate the expansion/collapse of the results container in the 50102 * horizontal direction. 50103 * 50104 * @property animHoriz 50105 * @type Boolean 50106 * @default false 50107 */ 50108YAHOO.widget.AutoComplete.prototype.animHoriz = false; 50109 50110/** 50111 * Whether or not to animate the expansion/collapse of the results container in the 50112 * vertical direction. 50113 * 50114 * @property animVert 50115 * @type Boolean 50116 * @default true 50117 */ 50118YAHOO.widget.AutoComplete.prototype.animVert = true; 50119 50120/** 50121 * Speed of container expand/collapse animation, in seconds.. 50122 * 50123 * @property animSpeed 50124 * @type Number 50125 * @default 0.3 50126 */ 50127YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3; 50128 50129/** 50130 * Whether or not to force the user's selection to match one of the query 50131 * results. Enabling this feature essentially transforms the input field into a 50132 * <select> field. This feature is not recommended with delimiter character(s) 50133 * defined. 50134 * 50135 * @property forceSelection 50136 * @type Boolean 50137 * @default false 50138 */ 50139YAHOO.widget.AutoComplete.prototype.forceSelection = false; 50140 50141/** 50142 * Whether or not to allow browsers to cache user-typed input in the input 50143 * field. Disabling this feature will prevent the widget from setting the 50144 * autocomplete="off" on the input field. When autocomplete="off" 50145 * and users click the back button after form submission, user-typed input can 50146 * be prefilled by the browser from its cache. This caching of user input may 50147 * not be desired for sensitive data, such as credit card numbers, in which 50148 * case, implementers should consider setting allowBrowserAutocomplete to false. 50149 * 50150 * @property allowBrowserAutocomplete 50151 * @type Boolean 50152 * @default true 50153 */ 50154YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true; 50155 50156/** 50157 * Enabling this feature prevents the toggling of the container to a collapsed state. 50158 * Setting to true does not automatically trigger the opening of the container. 50159 * Implementers are advised to pre-load the container with an explicit "sendQuery()" call. 50160 * 50161 * @property alwaysShowContainer 50162 * @type Boolean 50163 * @default false 50164 */ 50165YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false; 50166 50167/** 50168 * Whether or not to use an iFrame to layer over Windows form elements in 50169 * IE. Set to true only when the results container will be on top of a 50170 * <select> field in IE and thus exposed to the IE z-index bug (i.e., 50171 * 5.5 < IE < 7). 50172 * 50173 * @property useIFrame 50174 * @type Boolean 50175 * @default false 50176 */ 50177YAHOO.widget.AutoComplete.prototype.useIFrame = false; 50178 50179/** 50180 * Whether or not the results container should have a shadow. 50181 * 50182 * @property useShadow 50183 * @type Boolean 50184 * @default false 50185 */ 50186YAHOO.widget.AutoComplete.prototype.useShadow = false; 50187 50188/** 50189 * Whether or not the input field should be updated with selections. 50190 * 50191 * @property suppressInputUpdate 50192 * @type Boolean 50193 * @default false 50194 */ 50195YAHOO.widget.AutoComplete.prototype.suppressInputUpdate = false;
50196 50197/** 50198 * For backward compatibility to pre-2.6.0 formatResults() signatures, setting 50199 * resultsTypeList to true will take each object literal result returned by 50200 * DataSource and flatten into an array. 50201 * 50202 * @property resultTypeList 50203 * @type Boolean 50204 * @default true 50205 */ 50206YAHOO.widget.AutoComplete.prototype.resultTypeList = true; 50207 50208/** 50209 * For XHR DataSources, AutoComplete will automatically insert a "?" between the server URI and 50210 * the "query" param/value pair. To prevent this behavior, implementers should 50211 * set this value to false. To more fully customize the query syntax, implementers 50212 * should override the generateRequest() method. 50213 * 50214 * @property queryQuestionMark 50215 * @type Boolean 50216 * @default true 50217 */ 50218YAHOO.widget.AutoComplete.prototype.queryQuestionMark = true; 50219 50220/** 50221 * If true, before each time the container expands, the container element will be 50222 * positioned to snap to the bottom-left corner of the input element. If 50223 * autoSnapContainer is set to false, this positioning will not be done. 50224 * 50225 * @property autoSnapContainer 50226 * @type Boolean 50227 * @default true 50228 */ 50229YAHOO.widget.AutoComplete.prototype.autoSnapContainer = true; 50230 50231///////////////////////////////////////////////////////////////////////////// 50232// 50233// Public methods 50234// 50235///////////////////////////////////////////////////////////////////////////// 50236 50237 /** 50238 * Public accessor to the unique name of the AutoComplete instance. 50239 * 50240 * @method toString 50241 * @return {String} Unique name of the AutoComplete instance. 50242 */ 50243YAHOO.widget.AutoComplete.prototype.toString = function() { 50244 return "AutoComplete " + this._sName; 50245}; 50246 50247 /** 50248 * Returns DOM reference to input element. 50249 * 50250 * @method getInputEl 50251 * @return {HTMLELement} DOM reference to input element. 50252 */ 50253YAHOO.widget.AutoComplete.prototype.getInputEl = function() { 50254 return this._elTextbox; 50255}; 50256 50257 /** 50258 * Returns DOM reference to container element. 50259 * 50260 * @method getContainerEl 50261 * @return {HTMLELement} DOM reference to container element. 50262 */ 50263YAHOO.widget.AutoComplete.prototype.getContainerEl = function() { 50264 return this._elContainer; 50265}; 50266 50267 /** 50268 * Returns true if widget instance is currently active. 50269 * 50270 * @method isFocused 50271 * @return {Boolean} Returns true if widget instance is currently active. 50272 */ 50273YAHOO.widget.AutoComplete.prototype.isFocused = function() { 50274 return this._bFocused; 50275}; 50276 50277 /** 50278 * Returns true if container is in an expanded state, false otherwise. 50279 * 50280 * @method isContainerOpen 50281 * @return {Boolean} Returns true if container is in an expanded state, false otherwise. 50282 */ 50283YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() { 50284 return this._bContainerOpen; 50285}; 50286 50287/** 50288 * Public accessor to the <ul> element that displays query results within the results container. 50289 * 50290 * @method getListEl 50291 * @return {HTMLElement[]} Reference to <ul> element within the results container. 50292 */ 50293YAHOO.widget.AutoComplete.prototype.getListEl = function() { 50294 return this._elList; 50295}; 50296 50297/** 50298 * Public accessor to the matching string associated with a given <li> result. 50299 * 50300 * @method getListItemMatch 50301 * @param elListItem {HTMLElement} Reference to <LI> element. 50302 * @return {String} Matching string. 50303 */ 50304YAHOO.widget.AutoComplete.prototype.getListItemMatch = function(elListItem) { 50305 if(elListItem._sResultMatch) { 50306 return elListItem._sResultMatch; 50307 } 50308 else { 50309 return null; 50310 } 50311}; 50312 50313/** 50314 * Public accessor to the result data associated with a given <li> result. 50315 * 50316 * @method getListItemData 50317 * @param elListItem {HTMLElement} Reference to <LI> element. 50318 * @return {Object} Result data. 50319 */ 50320YAHOO.widget.AutoComplete.prototype.getListItemData = function(elListItem) { 50321 if(elListItem._oResultData) { 50322 return elListItem._oResultData; 50323 } 50324 else { 50325 return null; 50326 } 50327}; 50328 50329/** 50330 * Public accessor to the index of the associated with a given <li> result. 50331 * 50332 * @method getListItemIndex 50333 * @param elListItem {HTMLElement} Reference to <LI> element. 50334 * @return {Number} Index. 50335 */ 50336YAHOO.widget.AutoComplete.prototype.getListItemIndex = function(elListItem) { 50337 if(YAHOO.lang.isNumber(elListItem._nItemIndex)) { 50338 return elListItem._nItemIndex; 50339 } 50340 else { 50341 return null; 50342 } 50343}; 50344 50345/** 50346 * Sets HTML markup for the results container header. This markup will be 50347 * inserted within a <div>
50347 tag with a class of "yui-ac-hd". 50348 * 50349 * @method setHeader 50350 * @param sHeader {HTML} HTML markup for results container header. 50351 */ 50352YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) { 50353 if(this._elHeader) { 50354 var elHeader = this._elHeader; 50355 if(sHeader) { 50356 elHeader.innerHTML = sHeader; 50357 elHeader.style.display = ""; 50358 } 50359 else { 50360 elHeader.innerHTML = ""; 50361 elHeader.style.display = "none"; 50362 } 50363 } 50364}; 50365 50366/** 50367 * Sets HTML markup for the results container footer. This markup will be 50368 * inserted within a <div> tag with a class of "yui-ac-ft". 50369 * 50370 * @method setFooter 50371 * @param sFooter {HTML} HTML markup for results container footer. 50372 */ 50373YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) { 50374 if(this._elFooter) { 50375 var elFooter = this._elFooter; 50376 if(sFooter) { 50377 elFooter.innerHTML = sFooter; 50378 elFooter.style.display = ""; 50379 } 50380 else { 50381 elFooter.innerHTML = ""; 50382 elFooter.style.display = "none"; 50383 } 50384 } 50385}; 50386 50387/** 50388 * Sets HTML markup for the results container body. This markup will be 50389 * inserted within a <div> tag with a class of "yui-ac-bd". 50390 * 50391 * @method setBody 50392 * @param sBody {HTML} HTML markup for results container body. 50393 */ 50394YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) { 50395 if(this._elBody) { 50396 var elBody = this._elBody; 50397 YAHOO.util.Event.purgeElement(elBody, true); 50398 if(sBody) { 50399 elBody.innerHTML = sBody; 50400 elBody.style.display = ""; 50401 } 50402 else { 50403 elBody.innerHTML = ""; 50404 elBody.style.display = "none"; 50405 } 50406 this._elList = null; 50407 } 50408}; 50409 50410/** 50411* A function that converts an AutoComplete query into a request value which is then 50412* passed to the DataSource's sendRequest method in order to retrieve data for 50413* the query. By default, returns a String with the syntax: "query={query}" 50414* Implementers can customize this method for custom request syntaxes. 50415* 50416* @method generateRequest 50417* @param sQuery {String} Query string 50418* @return {MIXED} Request 50419*/ 50420YAHOO.widget.AutoComplete.prototype.generateRequest = function(sQuery) { 50421 var dataType = this.dataSource.dataType; 50422 50423 // Transform query string in to a request for remote data 50424 // By default, local data doesn't need a transformation, just passes along the query as is. 50425 if(dataType === YAHOO.util.DataSourceBase.TYPE_XHR) { 50426 // By default, XHR GET requests look like "{scriptURI}?{scriptQueryParam}={sQuery}&{scriptQueryAppend}" 50427 if(!this.dataSource.connMethodPost) { 50428 sQuery = (this.queryQuestionMark ? "?" : "") + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 50429 (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : ""); 50430 } 50431 // By default, XHR POST bodies are sent to the {scriptURI} like "{scriptQueryParam}={sQuery}&{scriptQueryAppend}" 50432 else { 50433 sQuery = (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 50434 (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : ""); 50435 } 50436 } 50437 // By default, remote script node requests look like "{scriptURI}&{scriptCallbackParam}={callbackString}&{scriptQueryParam}={sQuery}&{scriptQueryAppend}" 50438 else if(dataType === YAHOO.util.DataSourceBase.TYPE_SCRIPTNODE) { 50439 sQuery = "&" + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 50440 (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : ""); 50441 } 50442 50443 return sQuery; 50444}; 50445 50446/** 50447 * Makes query request to the DataSource. 50448 * 50449 * @method sendQuery 50450 * @param sQuery {String} Query string. 50451 */ 50452YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) { 50453 // Activate focus for a new interaction 50454 this._bFocused = true; 50455 50456 // Adjust programatically sent queries to look like they were input by user 50457 // when delimiters are enabled 50458 var newQuery = (this.delimChar) ? this._elTextbox.value + sQuery : sQuery; 50459 this._sendQuery(newQuery); 50460}; 50461 50462/** 50463 * Snaps container to bottom-left corner of input element 50464 * 50465 * @method snapContainer 50466 */ 50467YAHOO.widget.AutoComplete.prototype.snapContainer = function() { 50468 var oTextbox = this._elTextbox, 50469 pos = YAHOO.util.Dom.getXY(oTextbox); 50470 pos[1] += YAHOO.util.Dom.get(oTextbox).offsetHeight + 2; 50471 YAHOO.util.Dom.setXY(this._elContainer,pos); 50472}; 50473 50474/** 50475 * Expands container. 50476 * 50477 * @method expandContainer 50478 */ 50479YAHOO.widget.AutoComplete.prototype.expandContainer = function() { 50480 this._toggleContainer(true); 50481}; 50482 50483/** 50484 * Collapses container. 50485 * 50486 * @method collapseContainer 50487 */ 50488YAHOO.widget.AutoComplete.prototype.collapseContainer = function() { 50489 this._toggleContainer(false); 50490}; 50491 50492/** 50493 * Clears entire list of suggestions. 50494 * 50495 * @method clearList 50496 */ 50497YAHOO.widget.AutoComplete.prototype.clearList = function() { 50498 var allItems = this._elList.childNodes, 50499 i=allItems.length-1; 50500 for(; i>-1; i--) { 50501 allItems[i].style.display = "none"; 50502 } 50503}; 50504 50505/** 50506 * Handles subset matching for when queryMatchSubset is enabled. 50507 * 50508 * @method getSubsetMatches 50509 * @param sQuery {String} Query string. 50510 * @return {Object} oParsedResponse or null. 50511 */ 50512YAHOO.widget.AutoComplete.prototype.getSubsetMatches = function(sQuery) { 50513 var subQuery, oCachedResponse, subRequest; 50514 // Loop through substrings of each cached element's query property... 50515 for(var i = sQuery.length; i >= this.minQueryLength ; i--) { 50516 subRequest = this.generateRequest(sQuery.substr(0,i)); 50517 this.dataRequestEvent.fire(this, subQuery, subRequest); 50518 YAHOO.log("Searching for query subset \"" + subQuery + "\" in cache", "info", this.toString()); 50519 50520 // If a substring of the query is found in the cache 50521 oCachedResponse = this.dataSource.getCachedResponse(subRequest); 50522 if(oCachedResponse) { 50523 YAHOO.log("Found match for query subset \"" + subQuery + "\": " + YAHOO.lang.dump(oCachedResponse), "info", this.toString()); 50524 return this.filterResults.apply(this.dataSource, [sQuery, oCachedResponse, oCachedResponse, {scope:this}]); 50525 } 50526 }
50527 YAHOO.log("Did not find subset match for query subset \"" + sQuery + "\"" , "info", this.toString()); 50528 return null; 50529}; 50530 50531/** 50532 * Executed by DataSource (within DataSource scope via doBeforeParseData()) to 50533 * handle responseStripAfter cleanup. 50534 * 50535 * @method preparseRawResponse 50536 * @param sQuery {String} Query string. 50537 * @return {Object} oParsedResponse or null. 50538 */ 50539YAHOO.widget.AutoComplete.prototype.preparseRawResponse = function(oRequest, oFullResponse, oCallback) { 50540 var nEnd = ((this.responseStripAfter !== "") && (oFullResponse.indexOf)) ? 50541 oFullResponse.indexOf(this.responseStripAfter) : -1; 50542 if(nEnd != -1) { 50543 oFullResponse = oFullResponse.substring(0,nEnd); 50544 } 50545 return oFullResponse; 50546}; 50547 50548/** 50549 * Executed by DataSource (within DataSource scope via doBeforeCallback()) to 50550 * filter results through a simple client-side matching algorithm. 50551 * 50552 * @method filterResults 50553 * @param sQuery {String} Original request. 50554 * @param oFullResponse {Object} Full response object. 50555 * @param oParsedResponse {Object} Parsed response object. 50556 * @param oCallback {Object} Callback object. 50557 * @return {Object} Filtered response object. 50558 */ 50559 50560YAHOO.widget.AutoComplete.prototype.filterResults = function(sQuery, oFullResponse, oParsedResponse, oCallback) { 50561 // If AC has passed a query string value back to itself, grab it 50562 if(oCallback && oCallback.argument && YAHOO.lang.isValue(oCallback.argument.query)) { 50563 sQuery = oCallback.argument.query; 50564 } 50565 50566 // Only if a query string is available to match against 50567 if(sQuery && sQuery !== "") { 50568 // First make a copy of the oParseResponse 50569 oParsedResponse = YAHOO.widget.AutoComplete._cloneObject(oParsedResponse); 50570 50571 var oAC = oCallback.scope, 50572 oDS = this, 50573 allResults = oParsedResponse.results, // the array of results 50574 filteredResults = [], // container for filtered results, 50575 nMax = oAC.maxResultsDisplayed, // max to find 50576 bMatchCase = (oDS.queryMatchCase || oAC.queryMatchCase), // backward compat 50577 bMatchContains = (oDS.queryMatchContains || oAC.queryMatchContains); // backward compat 50578 50579 // Loop through each result object... 50580 for(var i=0, len=allResults.length; i<len; i++) { 50581 var oResult = allResults[i]; 50582 50583 // Grab the data to match against from the result object... 50584 var sResult = null; 50585 50586 // Result object is a simple string already 50587 if(YAHOO.lang.isString(oResult)) { 50588 sResult = oResult; 50589 } 50590 // Result object is an array of strings 50591 else if(YAHOO.lang.isArray(oResult)) { 50592 sResult = oResult[0]; 50593 50594 } 50595 // Result object is an object literal of strings 50596 else if(this.responseSchema.fields) { 50597 var key = this.responseSchema.fields[0].key || this.responseSchema.fields[0]; 50598 sResult = oResult[key]; 50599 } 50600 // Backwards compatibility 50601 else if(this.key) { 50602 sResult = oResult[this.key]; 50603 } 50604 50605 if(YAHOO.lang.isString(sResult)) { 50606 50607 var sKeyIndex = (bMatchCase) ? 50608 sResult.indexOf(decodeURIComponent(sQuery)) : 50609 sResult.toLowerCase().indexOf(decodeURIComponent(sQuery).toLowerCase()); 50610 50611 // A STARTSWITH match is when the query is found at the beginning of the key string... 50612 if((!bMatchContains && (sKeyIndex === 0)) || 50613 // A CONTAINS match is when the query is found anywhere within the key string... 50614 (bMatchContains && (sKeyIndex > -1))) { 50615 // Stash the match 50616 filteredResults.push(oResult); 50617 } 50618 } 50619 50620 // Filter no more if maxResultsDisplayed is reached 50621 if(len>nMax && filteredResults.length===nMax) { 50622 break; 50623 } 50624 } 50625 oParsedResponse.results = filteredResults; 50626 YAHOO.log("Filtered " + filteredResults.length + " results against query \"" + sQuery + "\": " + YAHOO.lang.dump(filteredResults), "info", this.toString()); 50627 } 50628 else {
50629 YAHOO.log("Did not filter results against query", "info", this.toString()); 50630 } 50631 50632 return oParsedResponse; 50633}; 50634 50635/** 50636 * Handles response for display. This is the callback function method passed to 50637 * YAHOO.util.DataSourceBase#sendRequest so results from the DataSource are 50638 * returned to the AutoComplete instance. 50639 * 50640 * @method handleResponse 50641 * @param sQuery {String} Original request. 50642 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 50643 * @param oPayload {MIXED} (optional) Additional argument(s) 50644 */ 50645YAHOO.widget.AutoComplete.prototype.handleResponse = function(sQuery, oResponse, oPayload) { 50646 if((this instanceof YAHOO.widget.AutoComplete) && this._sName) { 50647 this._populateList(sQuery, oResponse, oPayload); 50648 } 50649}; 50650 50651/** 50652 * Overridable method called before container is loaded with result data. 50653 * 50654 * @method doBeforeLoadData 50655 * @param sQuery {String} Original request. 50656 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 50657 * @param oPayload {MIXED} (optional) Additional argument(s) 50658 * @return {Boolean} Return true to continue loading data, false to cancel. 50659 */ 50660YAHOO.widget.AutoComplete.prototype.doBeforeLoadData = function(sQuery, oResponse, oPayload) { 50661 return true; 50662}; 50663 50664/** 50665 * Overridable method that returns HTML markup for one result to be populated 50666 * as innerHTML of an <LI> element. 50667 * 50668 * @method formatResult 50669 * @param oResultData {Object} Result data object. 50670 * @param sQuery {String} The corresponding query string. 50671 * @param sResultMatch {HTMLElement} The current query string. 50672 * @return {HTML} HTML markup of formatted result data. 50673 */ 50674YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultData, sQuery, sResultMatch) { 50675 var sMarkup = (sResultMatch) ? sResultMatch : ""; 50676 return sMarkup; 50677}; 50678 50679/** 50680 * An alternative to the formatResult() method, escapes the result data before 50681 * inserting into DOM. Implementers should point to this method when accessing 50682 * data from third-party sources, from user input, or from otherwise 50683 * untrustworthy sources: 50684 * myAutoComplete.formatResult = myAutoComplete.formatEscapedResult; 50685 * 50686 * @method formatEscapedResult 50687 * @param oResultData {Object} Result data object. 50688 * @param sQuery {String} The corresponding query string. 50689 * @param sResultMatch {HTMLElement} The current query string. 50690 * @return {String} Formatted result data. 50691 */ 50692YAHOO.widget.AutoComplete.prototype.formatEscapedResult = function(oResultData, sQuery, sResultMatch) { 50693 var sResult = (sResultMatch) ? sResultMatch : ""; 50694 return YAHOO.lang.escapeHTML(sResult); 50695}; 50696 50697/** 50698 * Overridable method called before container expands allows implementers to access data 50699 * and DOM elements. 50700 * 50701 * @method doBeforeExpandContainer 50702 * @param elTextbox {HTMLElement} The text input box. 50703 * @param elContainer {HTMLElement} The container element. 50704 * @param sQuery {String} The query string. 50705 * @param aResults {Object[]} An array of query results. 50706 * @return {Boolean} Return true to continue expanding container, false to cancel the expand. 50707 */ 50708YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(elTextbox, elContainer, sQuery, aResults) { 50709 return true; 50710}; 50711 50712 50713/** 50714 * Nulls out the entire AutoComplete instance and related objects, removes attached 50715 * event listeners, and clears out DOM elements inside the container. After 50716 * calling this method, the instance reference should be expliclitly nulled by 50717 * implementer, as in myAutoComplete = null. Use with caution! 50718 * 50719 * @method destroy 50720 */ 50721YAHOO.widget.AutoComplete.prototype.destroy = function() { 50722 var instanceName = this.toString(); 50723 var elInput = this._elTextbox; 50724 var elContainer = this._elContainer; 50725 50726 // Unhook custom events 50727 this.textboxFocusEvent.unsubscribeAll(); 50728 this.textboxKeyEvent.unsubscribeAll(); 50729 this.dataRequestEvent.unsubscribeAll(); 50730 this.dataReturnEvent.unsubscribeAll(); 50731 this.dataErrorEvent.unsubscribeAll(); 50732 this.containerPopulateEvent.unsubscribeAll(); 50733 this.containerExpandEvent.unsubscribeAll(); 50734 this.typeAheadEvent.unsubscribeAll(); 50735 this.itemMouseOverEvent.unsubscribeAll(); 50736 this.itemMouseOutEvent.unsubscribeAll(); 50737 this.itemArrowToEvent.unsubscribeAll(); 50738 this.itemArrowFromEvent.unsubscribeAll(); 50739 this.itemSelectEvent.unsubscribeAll(); 50740 this.unmatchedItemSelectEvent.unsubscribeAll(); 50741 this.selectionEnforceEvent.unsubscribeAll(); 50742 this.containerCollapseEvent.unsubscribeAll(); 50743 this.textboxBlurEvent.unsubscribeAll(); 50744 this.textboxChangeEvent.unsubscribeAll(); 50745 50746 // Unhook DOM events 50747 YAHOO.util.Event.purgeElement(elInput, true); 50748 YAHOO.util.Event.purgeElement(elContainer, true); 50749 50750 // Remove DOM elements 50751 elContainer.innerHTML = ""; 50752 50753 // Null out objects 50754 for(var key in this) { 50755 if(YAHOO.lang.hasOwnProperty(this, key)) { 50756 this[key] = null; 50757 } 50758 } 50759 50760 YAHOO.log("AutoComplete instance destroyed: " + instanceName); 50761}; 50762 50763///////////////////////////////////////////////////////////////////////////// 50764// 50765// Public events 50766// 50767///////////////////////////////////////////////////////////////////////////// 50768 50769/** 50770 * Fired when the input field receives focus. 50771 * 50772 * @event textboxFocusEvent 50773 * @param type {String} Name of the event. 50774 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50775 */ 50776YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null; 50777 50778/** 50779 * Fired when the input field receives key input. 50780 * 50781 * @event textboxKeyEvent 50782 * @param type {String} Name of the event. 50783 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
50784 * @param args[1] {Number} The keycode number. 50785 */ 50786YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null; 50787 50788/** 50789 * Fired when the AutoComplete instance makes a request to the DataSource. 50790 * 50791 * @event dataRequestEvent 50792 * @param type {String} Name of the event. 50793 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50794 * @param args[1] {String} The query string. 50795 * @param args[2] {Object} The request. 50796 */ 50797YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null; 50798 50799/** 50800 * Fired when the AutoComplete request to the DataSource is canceled. 50801 * 50802 * @event dataRequestCancelEvent 50803 * @param type {String} Name of the event. 50804 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50805 * @param args[1] {String} The query string. 50806 */ 50807YAHOO.widget.AutoComplete.prototype.dataRequestCancelEvent = null; 50808 50809/** 50810 * Fired when the AutoComplete instance receives query results from the data 50811 * source. 50812 * 50813 * @event dataReturnEvent 50814 * @param type {String} Name of the event. 50815 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50816 * @param args[1] {String} The query string. 50817 * @param args[2] {Object[]} Results array. 50818 */ 50819YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null; 50820 50821/** 50822 * Fired when the AutoComplete instance does not receive query results from the 50823 * DataSource due to an error. 50824 * 50825 * @event dataErrorEvent 50826 * @param type {String} Name of the event. 50827 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50828 * @param args[1] {String} The query string. 50829 * @param args[2] {Object} The response object, if available. 50830 */ 50831YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null; 50832 50833/** 50834 * Fired when the results container is populated. 50835 * 50836 * @event containerPopulateEvent 50837 * @param type {String} Name of the event. 50838 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50839 */ 50840YAHOO.widget.AutoComplete.prototype.containerPopulateEvent = null; 50841 50842/** 50843 * Fired when the results container is expanded. 50844 * 50845 * @event containerExpandEvent 50846 * @param type {String} Name of the event. 50847 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50848 */ 50849YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null; 50850 50851/** 50852 * Fired when the input field has been prefilled by the type-ahead 50853 * feature. 50854 * 50855 * @event typeAheadEvent 50856 * @param type {String} Name of the event. 50857 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50858 * @param args[1] {String} The query string. 50859 * @param args[2] {String} The prefill string. 50860 */ 50861YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null; 50862 50863/** 50864 * Fired when result item has been moused over. 50865 * 50866 * @event itemMouseOverEvent 50867 * @param type {String} Name of the event. 50868 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50869 * @param args[1] {HTMLElement} The <li> element item moused to. 50870 */ 50871YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null; 50872 50873/** 50874 * Fired when result item has been moused out. 50875 * 50876 * @event itemMouseOutEvent 50877 * @param type {String} Name of the event. 50878 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50879 * @param args[1] {HTMLElement} The <li> element item moused from. 50880 */ 50881YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null; 50882 50883/** 50884 * Fired when result item has been arrowed to. 50885 * 50886 * @event itemArrowToEvent 50887 * @param type {String} Name of the event. 50888 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50889 * @param args[1] {HTMLElement} The <li> element item arrowed to. 50890 */ 50891YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null; 50892 50893/** 50894 * Fired when result item has been arrowed away from. 50895 * 50896 * @event itemArrowFromEvent 50897 * @param type {String} Name of the event. 50898 * @param args[0[ {YAHOO.widget.AutoComplete} The AutoComplete instance. 50899 * @param args[1] {HTMLElement} The <li> element item arrowed from. 50900 */ 50901YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null; 50902 50903/** 50904 * Fired when an item is selected via mouse click, ENTER key, or TAB key. 50905 * 50906 * @event itemSelectEvent 50907 * @param type {String} Name of the event. 50908 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
50909 * @param args[1] {HTMLElement} The selected <li> element item. 50910 * @param args[2] {Object} The data returned for the item, either as an object, 50911 * or mapped from the schema into an array. 50912 */ 50913YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null; 50914 50915/** 50916 * Fired when a user selection does not match any of the displayed result items. 50917 * 50918 * @event unmatchedItemSelectEvent 50919 * @param type {String} Name of the event. 50920 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50921 * @param args[1] {String} The selected string. 50922 */ 50923YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null; 50924 50925/** 50926 * Fired if forceSelection is enabled and the user's input has been cleared 50927 * because it did not match one of the returned query results. 50928 * 50929 * @event selectionEnforceEvent 50930 * @param type {String} Name of the event. 50931 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50932 * @param args[1] {String} The cleared value (including delimiters if applicable). 50933 */ 50934YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null; 50935 50936/** 50937 * Fired when the results container is collapsed. 50938 * 50939 * @event containerCollapseEvent 50940 * @param type {String} Name of the event. 50941 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50942 */ 50943YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null; 50944 50945/** 50946 * Fired when the input field loses focus. 50947 * 50948 * @event textboxBlurEvent 50949 * @param type {String} Name of the event. 50950 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50951 */ 50952YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null; 50953 50954/** 50955 * Fired when the input field value has changed when it loses focus. 50956 * 50957 * @event textboxChangeEvent 50958 * @param type {String} Name of the event. 50959 * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance. 50960 */ 50961YAHOO.widget.AutoComplete.prototype.textboxChangeEvent = null; 50962 50963///////////////////////////////////////////////////////////////////////////// 50964// 50965// Private member variables 50966// 50967///////////////////////////////////////////////////////////////////////////// 50968 50969/** 50970 * Internal class variable to index multiple AutoComplete instances. 50971 * 50972 * @property _nIndex 50973 * @type Number 50974 * @default 0 50975 * @private 50976 */ 50977YAHOO.widget.AutoComplete._nIndex = 0; 50978 50979/** 50980 * Name of AutoComplete instance. 50981 * 50982 * @property _sName 50983 * @type String 50984 * @private 50985 */ 50986YAHOO.widget.AutoComplete.prototype._sName = null; 50987 50988/** 50989 * Text input field DOM element. 50990 * 50991 * @property _elTextbox 50992 * @type HTMLElement 50993 * @private 50994 */ 50995YAHOO.widget.AutoComplete.prototype._elTextbox = null; 50996 50997/** 50998 * Container DOM element. 50999 * 51000 * @property _elContainer 51001 * @type HTMLElement 51002 * @private 51003 */ 51004YAHOO.widget.AutoComplete.prototype._elContainer = null; 51005 51006/** 51007 * Reference to content element within container element. 51008 * 51009 * @property _elContent 51010 * @type HTMLElement 51011 * @private 51012 */ 51013YAHOO.widget.AutoComplete.prototype._elContent = null; 51014 51015/** 51016 * Reference to header element within content element. 51017 * 51018 * @property _elHeader 51019 * @type HTMLElement 51020 * @private 51021 */ 51022YAHOO.widget.AutoComplete.prototype._elHeader = null; 51023 51024/** 51025 * Reference to body element within content element. 51026 * 51027 * @property _elBody 51028 * @type HTMLElement 51029 * @private 51030 */ 51031YAHOO.widget.AutoComplete.prototype._elBody = null; 51032 51033/** 51034 * Reference to footer element within content element. 51035 * 51036 * @property _elFooter 51037 * @type HTMLElement 51038 * @private 51039 */ 51040YAHOO.widget.AutoComplete.prototype._elFooter = null; 51041 51042/** 51043 * Reference to shadow element within container element. 51044 * 51045 * @property _elShadow 51046 * @type HTMLElement 51047 * @private 51048 */ 51049YAHOO.widget.AutoComplete.prototype._elShadow = null; 51050 51051/** 51052 * Reference to iframe element within container element. 51053 * 51054 * @property _elIFrame 51055 * @type HTMLElement 51056 * @private 51057 */ 51058YAHOO.widget.AutoComplete.prototype._elIFrame = null; 51059 51060/** 51061 * Whether or not the widget instance is currently active. If query results come back 51062 * but the user has already moved on, do not proceed with auto complete behavior. 51063 * 51064 * @property _bFocused 51065 * @type Boolean 51066 * @private 51067 */ 51068YAHOO.widget.AutoComplete.prototype._bFocused = false; 51069 51070/** 51071 * Animation instance for container expand/collapse. 51072 * 51073 * @property _oAnim 51074 * @type Boolean 51075 * @private 51076 */ 51077YAHOO.widget.AutoComplete.prototype._oAnim = null; 51078 51079/** 51080 * Whether or not the results container is currently open. 51081 * 51082 * @property _bContainerOpen 51083 * @type Boolean 51084 * @private 51085 */ 51086YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
51087 51088/** 51089 * Whether or not the mouse is currently over the results 51090 * container. This is necessary in order to prevent clicks on container items 51091 * from being text input field blur events. 51092 * 51093 * @property _bOverContainer 51094 * @type Boolean 51095 * @private 51096 */ 51097YAHOO.widget.AutoComplete.prototype._bOverContainer = false; 51098 51099/** 51100 * Internal reference to <ul> elements that contains query results within the 51101 * results container. 51102 * 51103 * @property _elList 51104 * @type HTMLElement 51105 * @private 51106 */ 51107YAHOO.widget.AutoComplete.prototype._elList = null; 51108 51109/* 51110 * Array of <li> elements references that contain query results within the 51111 * results container. 51112 * 51113 * @property _aListItemEls 51114 * @type HTMLElement[] 51115 * @private 51116 */ 51117//YAHOO.widget.AutoComplete.prototype._aListItemEls = null; 51118 51119/** 51120 * Number of <li> elements currently displayed in results container. 51121 * 51122 * @property _nDisplayedItems 51123 * @type Number 51124 * @private 51125 */ 51126YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0; 51127 51128/* 51129 * Internal count of <li> elements displayed and hidden in results container. 51130 * 51131 * @property _maxResultsDisplayed 51132 * @type Number 51133 * @private 51134 */ 51135//YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0; 51136 51137/** 51138 * Current query string 51139 * 51140 * @property _sCurQuery 51141 * @type String 51142 * @private 51143 */ 51144YAHOO.widget.AutoComplete.prototype._sCurQuery = null; 51145 51146/** 51147 * Selections from previous queries (for saving delimited queries). 51148 * 51149 * @property _sPastSelections 51150 * @type String 51151 * @default "" 51152 * @private 51153 */ 51154YAHOO.widget.AutoComplete.prototype._sPastSelections = ""; 51155 51156/** 51157 * Stores initial input value used to determine if textboxChangeEvent should be fired. 51158 * 51159 * @property _sInitInputValue 51160 * @type String 51161 * @private 51162 */ 51163YAHOO.widget.AutoComplete.prototype._sInitInputValue = null; 51164 51165/** 51166 * Pointer to the currently highlighted <li> element in the container. 51167 * 51168 * @property _elCurListItem 51169 * @type HTMLElement 51170 * @private 51171 */ 51172YAHOO.widget.AutoComplete.prototype._elCurListItem = null; 51173 51174/** 51175 * Pointer to the currently pre-highlighted <li> element in the container. 51176 * 51177 * @property _elCurPrehighlightItem 51178 * @type HTMLElement 51179 * @private 51180 */ 51181YAHOO.widget.AutoComplete.prototype._elCurPrehighlightItem = null; 51182 51183/** 51184 * Whether or not an item has been selected since the container was populated 51185 * with results. Reset to false by _populateList, and set to true when item is 51186 * selected. 51187 * 51188 * @property _bItemSelected 51189 * @type Boolean 51190 * @private 51191 */ 51192YAHOO.widget.AutoComplete.prototype._bItemSelected = false; 51193 51194/** 51195 * Key code of the last key pressed in textbox. 51196 * 51197 * @property _nKeyCode 51198 * @type Number 51199 * @private 51200 */ 51201YAHOO.widget.AutoComplete.prototype._nKeyCode = null; 51202 51203/** 51204 * Delay timeout ID. 51205 * 51206 * @property _nDelayID 51207 * @type Number 51208 * @private 51209 */ 51210YAHOO.widget.AutoComplete.prototype._nDelayID = -1; 51211 51212/** 51213 * TypeAhead delay timeout ID. 51214 * 51215 * @property _nTypeAheadDelayID 51216 * @type Number 51217 * @private 51218 */ 51219YAHOO.widget.AutoComplete.prototype._nTypeAheadDelayID = -1; 51220 51221/** 51222 * Src to iFrame used when useIFrame = true. Supports implementations over SSL 51223 * as well. 51224 * 51225 * @property _iFrameSrc 51226 * @type String 51227 * @private 51228 */ 51229YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;"; 51230 51231/** 51232 * For users typing via certain IMEs, queries must be triggered by intervals, 51233 * since key events yet supported across all browsers for all IMEs. 51234 * 51235 * @property _queryInterval 51236 * @type Object 51237 * @private 51238 */ 51239YAHOO.widget.AutoComplete.prototype._queryInterval = null; 51240 51241/** 51242 * Internal tracker to last known textbox value, used to determine whether or not 51243 * to trigger a query via interval for certain IME users. 51244 * 51245 * @event _sLastTextboxValue 51246 * @type String 51247 * @private 51248 */ 51249YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null; 51250 51251///////////////////////////////////////////////////////////////////////////// 51252// 51253// Private methods 51254// 51255///////////////////////////////////////////////////////////////////////////// 51256 51257/** 51258 * Updates and validates latest public config properties. 51259 * 51260 * @method __initProps 51261 * @private 51262 */ 51263YAHOO.widget.AutoComplete.prototype._initProps = function() {
51264 // Correct any invalid values 51265 var minQueryLength = this.minQueryLength; 51266 if(!YAHOO.lang.isNumber(minQueryLength)) { 51267 this.minQueryLength = 1; 51268 } 51269 var maxResultsDisplayed = this.maxResultsDisplayed; 51270 if(!YAHOO.lang.isNumber(maxResultsDisplayed) || (maxResultsDisplayed < 1)) { 51271 this.maxResultsDisplayed = 10; 51272 } 51273 var queryDelay = this.queryDelay; 51274 if(!YAHOO.lang.isNumber(queryDelay) || (queryDelay < 0)) { 51275 this.queryDelay = 0.2; 51276 } 51277 var typeAheadDelay = this.typeAheadDelay; 51278 if(!YAHOO.lang.isNumber(typeAheadDelay) || (typeAheadDelay < 0)) { 51279 this.typeAheadDelay = 0.2; 51280 } 51281 var delimChar = this.delimChar; 51282 if(YAHOO.lang.isString(delimChar) && (delimChar.length > 0)) { 51283 this.delimChar = [delimChar]; 51284 } 51285 else if(!YAHOO.lang.isArray(delimChar)) { 51286 this.delimChar = null; 51287 } 51288 var animSpeed = this.animSpeed; 51289 if((this.animHoriz || this.animVert) && YAHOO.util.Anim) { 51290 if(!YAHOO.lang.isNumber(animSpeed) || (animSpeed < 0)) { 51291 this.animSpeed = 0.3; 51292 } 51293 if(!this._oAnim ) { 51294 this._oAnim = new YAHOO.util.Anim(this._elContent, {}, this.animSpeed); 51295 } 51296 else { 51297 this._oAnim.duration = this.animSpeed; 51298 } 51299 } 51300 if(this.forceSelection && delimChar) { 51301 YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString()); 51302 } 51303}; 51304 51305/** 51306 * Initializes the results container helpers if they are enabled and do 51307 * not exist 51308 * 51309 * @method _initContainerHelperEls 51310 * @private 51311 */ 51312YAHOO.widget.AutoComplete.prototype._initContainerHelperEls = function() { 51313 if(this.useShadow && !this._elShadow) { 51314 var elShadow = document.createElement("div"); 51315 elShadow.className = "yui-ac-shadow"; 51316 elShadow.style.width = 0; 51317 elShadow.style.height = 0; 51318 this._elShadow = this._elContainer.appendChild(elShadow); 51319 } 51320 if(this.useIFrame && !this._elIFrame) { 51321 var elIFrame = document.createElement("iframe"); 51322 elIFrame.src = this._iFrameSrc; 51323 elIFrame.frameBorder = 0; 51324 elIFrame.scrolling = "no"; 51325 elIFrame.style.position = "absolute"; 51326 elIFrame.style.width = 0; 51327 elIFrame.style.height = 0; 51328 elIFrame.style.padding = 0; 51329 elIFrame.tabIndex = -1; 51330 elIFrame.role = "presentation"; 51331 elIFrame.title = "Presentational iframe shim"; 51332 this._elIFrame = this._elContainer.appendChild(elIFrame); 51333 } 51334}; 51335 51336/** 51337 * Initializes the results container once at object creation 51338 * 51339 * @method _initContainerEl 51340 * @private 51341 */ 51342YAHOO.widget.AutoComplete.prototype._initContainerEl = function() { 51343 YAHOO.util.Dom.addClass(this._elContainer, "yui-ac-container"); 51344 51345 if(!this._elContent) { 51346 // The elContent div is assigned DOM listeners and 51347 // helps size the iframe and shadow properly 51348 var elContent = document.createElement("div"); 51349 elContent.className = "yui-ac-content"; 51350 elContent.style.display = "none"; 51351 51352 this._elContent = this._elContainer.appendChild(elContent); 51353 51354 var elHeader = document.createElement("div"); 51355 elHeader.className = "yui-ac-hd"; 51356 elHeader.style.display = "none"; 51357 this._elHeader = this._elContent.appendChild(elHeader); 51358 51359 var elBody = document.createElement("div"); 51360 elBody.className = "yui-ac-bd"; 51361 this._elBody = this._elContent.appendChild(elBody); 51362 51363 var elFooter = document.createElement("div"); 51364 elFooter.className = "yui-ac-ft"; 51365 elFooter.style.display = "none"; 51366 this._elFooter = this._elContent.appendChild(elFooter); 51367 } 51368 else { 51369 YAHOO.log("Could not initialize the container","warn",this.toString()); 51370 } 51371}; 51372 51373/** 51374 * Clears out contents of container body and creates up to 51375 * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an 51376 * <ul> element. 51377 * 51378 * @method _initListEl 51379 * @private 51380 */ 51381YAHOO.widget.AutoComplete.prototype._initListEl = function() { 51382 var nListLength = this.maxResultsDisplayed, 51383 elList = this._elList || document.createElement("ul"), 51384 elListItem; 51385 51386 while(elList.childNodes.length < nListLength) { 51387 elListItem = document.createElement("li"); 51388 elListItem.style.display = "none"; 51389 elListItem._nItemIndex = elList.childNodes.length; 51390 elList.appendChild(elListItem); 51391 } 51392 if(!this._elList) { 51393 var elBody = this._elBody; 51394 YAHOO.util.Event.purgeElement(elBody, true); 51395 elBody.innerHTML = ""; 51396 this._elList = elBody.appendChild(elList); 51397 } 51398 51399 this._elBody.style.display = ""; 51400}; 51401 51402/** 51403 * Focuses input field. 51404 * 51405 * @method _focus 51406 * @private 51407 */ 51408YAHOO.widget.AutoComplete.prototype._focus = function() { 51409 // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets 51410 var oSelf = this; 51411 setTimeout(function() { 51412 try { 51413 oSelf._elTextbox.focus(); 51414 } 51415 catch(e) { 51416 } 51417 },0); 51418}; 51419 51420/** 51421 * Enables interval detection for IME support. 51422 * 51423 * @method _enableIntervalDetection 51424 * @private 51425 */ 51426YAHOO.widget.AutoComplete.prototype._enableIntervalDetection = function() { 51427 var oSelf = this; 51428 if(!oSelf._queryInterval && oSelf.queryInterval) { 51429 oSelf._queryInterval = setInterval(function() { oSelf._onInterval(); }, oSelf.queryInterval); 51430 YAHOO.log("Interval set", "info", this.toString()); 51431 } 51432}; 51433 51434/** 51435 * Enables interval detection for a less performant but brute force mechanism to 51436 * detect input values at an interval set by queryInterval and send queries if 51437 * input value has changed. Needed to support right-click+paste or shift+insert
51438 * edge cases. Please note that intervals are cleared at the end of each interaction, 51439 * so enableIntervalDetection must be called for each new interaction. The 51440 * recommended approach is to call it in response to textboxFocusEvent. 51441 * 51442 * @method enableIntervalDetection 51443 */ 51444YAHOO.widget.AutoComplete.prototype.enableIntervalDetection = 51445 YAHOO.widget.AutoComplete.prototype._enableIntervalDetection; 51446 51447/** 51448 * Enables query triggers based on text input detection by intervals (rather 51449 * than by key events). 51450 * 51451 * @method _onInterval 51452 * @private 51453 */ 51454YAHOO.widget.AutoComplete.prototype._onInterval = function() { 51455 var currValue = this._elTextbox.value; 51456 var lastValue = this._sLastTextboxValue; 51457 if(currValue != lastValue) { 51458 this._sLastTextboxValue = currValue; 51459 this._sendQuery(currValue); 51460 } 51461}; 51462 51463/** 51464 * Cancels text input detection by intervals. 51465 * 51466 * @method _clearInterval 51467 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 51468 * @private 51469 */ 51470YAHOO.widget.AutoComplete.prototype._clearInterval = function() { 51471 if(this._queryInterval) { 51472 clearInterval(this._queryInterval); 51473 this._queryInterval = null; 51474 YAHOO.log("Interval cleared", "info", this.toString()); 51475 } 51476}; 51477 51478/** 51479 * Whether or not key is functional or should be ignored. Note that the right 51480 * arrow key is NOT an ignored key since it triggers queries for certain intl 51481 * charsets. 51482 * 51483 * @method _isIgnoreKey 51484 * @param nKeycode {Number} Code of key pressed. 51485 * @return {Boolean} True if key should be ignored, false otherwise. 51486 * @private 51487 */ 51488YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) { 51489 if((nKeyCode == 9) || (nKeyCode == 13) || // tab, enter 51490 (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl 51491 (nKeyCode >= 18 && nKeyCode <= 20) || // alt, pause/break,caps lock 51492 (nKeyCode == 27) || // esc 51493 (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end 51494 /*(nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up 51495 (nKeyCode == 40) || // down*/ 51496 (nKeyCode >= 36 && nKeyCode <= 40) || // home,left,up, right, down 51497 (nKeyCode >= 44 && nKeyCode <= 45) || // print screen,insert 51498 (nKeyCode == 229) // Bug 2041973: Korean XP fires 2 keyup events, the key and 229 51499 ) { 51500 return true; 51501 } 51502 return false; 51503}; 51504 51505/** 51506 * Makes query request to the DataSource. 51507 * 51508 * @method _sendQuery 51509 * @param sQuery {String} Query string. 51510 * @private 51511 */ 51512YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) { 51513 // Widget has been effectively turned off 51514 if(this.minQueryLength < 0) { 51515 this._toggleContainer(false); 51516 YAHOO.log("Property minQueryLength is less than 0", "info", this.toString()); 51517 return; 51518 } 51519 // Delimiter has been enabled 51520 if(this.delimChar) { 51521 var extraction = this._extractQuery(sQuery); 51522 // Here is the query itself 51523 sQuery = extraction.query; 51524 // ...and save the rest of the string for later 51525 this._sPastSelections = extraction.previous; 51526 } 51527 51528 // Don't search queries that are too short 51529 if((sQuery && (sQuery.length < this.minQueryLength)) || (!sQuery && this.minQueryLength > 0)) { 51530 if(this._nDelayID != -1) { 51531 clearTimeout(this._nDelayID); 51532 } 51533 this._toggleContainer(false); 51534 YAHOO.log("Query \"" + sQuery + "\" is too short", "info", this.toString()); 51535 return; 51536 } 51537 51538 sQuery = encodeURIComponent(sQuery); 51539 this._nDelayID = -1; // Reset timeout ID because request is being made 51540 51541 // Subset matching 51542 if(this.dataSource.queryMatchSubset || this.queryMatchSubset) { // backward compat 51543 var oResponse = this.getSubsetMatches(sQuery); 51544 if(oResponse) { 51545 this.handleResponse(sQuery, oResponse, {query: sQuery}); 51546 return; 51547 } 51548 } 51549 51550 if(this.dataSource.responseStripAfter) { 51551 this.dataSource.doBeforeParseData = this.preparseRawResponse; 51552 } 51553 if(this.applyLocalFilter) { 51554 this.dataSource.doBeforeCallback = this.filterResults; 51555 } 51556 51557 var sRequest = this.generateRequest(sQuery); 51558 51559 if(sRequest !== undefined) { 51560 this.dataRequestEvent.fire(this, sQuery, sRequest); 51561 YAHOO.log("Sending query \"" + sRequest + "\"", "info", this.toString()); 51562 51563 this.dataSource.sendRequest(sRequest, { 51564 success : this.handleResponse, 51565 failure : this.handleResponse, 51566 scope : this, 51567 argument: { 51568 query: sQuery 51569 } 51570 }); 51571 } 51572 else { 51573 this.dataRequestCancelEvent.fire(this, sQuery); 51574 YAHOO.log("Canceled query \"" + sQuery + "\"", "info", this.toString()); 51575 } 51576}; 51577 51578/** 51579 * Populates the given <li> element with return value from formatResult(). 51580 * 51581 * @method _populateListItem 51582 * @param elListItem {HTMLElement} The LI element. 51583 * @param oResult {Object} The result object. 51584 * @param sCurQuery {String} The query string. 51585 * @private 51586 */ 51587YAHOO.widget.AutoComplete.prototype._populateListItem = function(elListItem, oResult, sQuery) { 51588 elListItem.innerHTML = this.formatResult(oResult, sQuery, elListItem._sResultMatch); 51589}; 51590 51591/** 51592 * Populates the array of <li> elements in the container with query 51593 * results. 51594 * 51595 * @method _populateList 51596 * @param sQuery {String} Original request. 51597 * @param oResponse {Object}
51597 <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 51598 * @param oPayload {MIXED} (optional) Additional argument(s) 51599 * @private 51600 */ 51601YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, oResponse, oPayload) { 51602 // Clear previous timeout 51603 if(this._nTypeAheadDelayID != -1) { 51604 clearTimeout(this._nTypeAheadDelayID); 51605 } 51606 51607 sQuery = (oPayload && oPayload.query) ? oPayload.query : sQuery; 51608 51609 // Pass data through abstract method for any transformations 51610 var ok = this.doBeforeLoadData(sQuery, oResponse, oPayload); 51611 51612 // Data is ok 51613 if(ok && !oResponse.error) { 51614 this.dataReturnEvent.fire(this, sQuery, oResponse.results); 51615 51616 // Continue only if instance is still active (i.e., user hasn't already moved on) 51617 if(this._bFocused) { 51618 // Store state for this interaction 51619 var sCurQuery = decodeURIComponent(sQuery); 51620 this._sCurQuery = sCurQuery; 51621 this._bItemSelected = false; 51622 51623 var allResults = oResponse.results, 51624 nItemsToShow = Math.min(allResults.length,this.maxResultsDisplayed), 51625 sMatchKey = (this.dataSource.responseSchema.fields) ? 51626 (this.dataSource.responseSchema.fields[0].key || this.dataSource.responseSchema.fields[0]) : 0; 51627 51628 if(nItemsToShow > 0) { 51629 // Make sure container and helpers are ready to go 51630 if(!this._elList || (this._elList.childNodes.length < nItemsToShow)) { 51631 this._initListEl(); 51632 } 51633 this._initContainerHelperEls(); 51634 51635 var allListItemEls = this._elList.childNodes; 51636 // Fill items with data from the bottom up 51637 for(var i = nItemsToShow-1; i >= 0; i--) { 51638 var elListItem = allListItemEls[i], 51639 oResult = allResults[i]; 51640 51641 // Backward compatibility 51642 if(this.resultTypeList) { 51643 // Results need to be converted back to an array 51644 var aResult = []; 51645 // Match key is first 51646 aResult[0] = (YAHOO.lang.isString(oResult)) ? oResult : oResult[sMatchKey] || oResult[this.key]; 51647 // Add additional data to the result array 51648 var fields = this.dataSource.responseSchema.fields; 51649 if(YAHOO.lang.isArray(fields) && (fields.length > 1)) { 51650 for(var k=1, len=fields.length; k<len; k++) { 51651 aResult[aResult.length] = oResult[fields[k].key || fields[k]]; 51652 } 51653 } 51654 // No specific fields defined, so pass along entire data object 51655 else { 51656 // Already an array 51657 if(YAHOO.lang.isArray(oResult)) { 51658 aResult = oResult; 51659 } 51660 // Simple string 51661 else if(YAHOO.lang.isString(oResult)) { 51662 aResult = [oResult]; 51663 } 51664 // Object 51665 else { 51666 aResult[1] = oResult; 51667 } 51668 } 51669 oResult = aResult; 51670 } 51671 51672 // The matching value, including backward compatibility for array format and safety net 51673 elListItem._sResultMatch = (YAHOO.lang.isString(oResult)) ? oResult : (YAHOO.lang.isArray(oResult)) ? oResult[0] : (oResult[sMatchKey] || ""); 51674 elListItem._oResultData = oResult; // Additional data 51675 this._populateListItem(elListItem, oResult, sCurQuery); 51676 elListItem.style.display = ""; 51677 } 51678 51679 // Clear out extraneous items 51680 if(nItemsToShow < allListItemEls.length) { 51681 var extraListItem; 51682 for(var j = allListItemEls.length-1; j >= nItemsToShow; j--) { 51683 extraListItem = allListItemEls[j]; 51684 extraListItem.style.display = "none"; 51685 } 51686 } 51687 51688 this._nDisplayedItems = nItemsToShow; 51689 51690 this.containerPopulateEvent.fire(this, sQuery, allResults); 51691 51692 // Highlight the first item 51693 if(this.autoHighlight) { 51694 var elFirstListItem = this._elList.firstChild; 51695 this._toggleHighlight(elFirstListItem,"to"); 51696 this.itemArrowToEvent.fire(this, elFirstListItem); 51697 YAHOO.log("Arrowed to first item", "info", this.toString()); 51698 this._typeAhead(elFirstListItem,sQuery); 51699 } 51700 // Unhighlight any previous time 51701 else { 51702 this._toggleHighlight(this._elCurListItem,"from"); 51703 } 51704 51705 // Pre-expansion stuff 51706 ok = this._doBeforeExpandContainer(this._elTextbox, this._elContainer, sQuery, allResults); 51707 51708 // Expand the container 51709 this._toggleContainer(ok); 51710 } 51711 else { 51712 this._toggleContainer(false); 51713 } 51714 51715 YAHOO.log("Container populated with " + nItemsToShow + " list items", "info", this.toString()); 51716 return; 51717 } 51718 } 51719 // Error 51720 else { 51721 this.dataErrorEvent.fire(this, sQuery, oResponse); 51722 } 51723 51724 YAHOO.log("Could not populate list", "info", this.toString()); 51725}; 51726 51727/** 51728 * Called before container expands, by default snaps container to the 51729 * bottom-left corner of the input element, then calls public overrideable method. 51730 * 51731 * @method _doBeforeExpandContainer 51732 * @param elTextbox {HTMLElement} The text input box. 51733 * @param elContainer {HTMLElement} The container element. 51734 * @param sQuery {String} The query string. 51735 * @param aResults {Object[]} An array of query results. 51736 * @return {Boolean} Return true to continue expanding container, false to cancel the expand. 51737 * @private 51738 */ 51739YAHOO.widget.AutoComplete.prototype._doBeforeExpandContainer = function(elTextbox, elContainer, sQuery, aResults) { 51740 if(this.autoSnapContainer) { 51741 this.snapContainer(); 51742 } 51743 51744 return this.doBeforeExpandContainer(elTextbox, elContainer, sQuery, aResults); 51745}; 51746 51747/** 51748 * When forceSelection is true and the user attempts 51749 * leave the text input box without selecting an item from the query results, 51750 * the user selection is cleared. 51751 *
51752 * @method _clearSelection 51753 * @private 51754 */ 51755YAHOO.widget.AutoComplete.prototype._clearSelection = function() { 51756 var extraction = (this.delimChar) ? this._extractQuery(this._elTextbox.value) : 51757 {previous:"",query:this._elTextbox.value}; 51758 this._elTextbox.value = extraction.previous; 51759 this.selectionEnforceEvent.fire(this, extraction.query); 51760 YAHOO.log("Selection enforced", "info", this.toString()); 51761}; 51762 51763/** 51764 * Whether or not user-typed value in the text input box matches any of the 51765 * query results. 51766 * 51767 * @method _textMatchesOption 51768 * @return {HTMLElement} Matching list item element if user-input text matches 51769 * a result, null otherwise. 51770 * @private 51771 */ 51772YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() { 51773 var elMatch = null; 51774 51775 for(var i=0; i<this._nDisplayedItems; i++) { 51776 var elListItem = this._elList.childNodes[i]; 51777 var sMatch = ("" + elListItem._sResultMatch).toLowerCase(); 51778 if(sMatch == this._sCurQuery.toLowerCase()) { 51779 elMatch = elListItem; 51780 break; 51781 } 51782 } 51783 return(elMatch); 51784}; 51785 51786/** 51787 * Updates in the text input box with the first query result as the user types, 51788 * selecting the substring that the user has not typed. 51789 * 51790 * @method _typeAhead 51791 * @param elListItem {HTMLElement} The <li> element item whose data populates the input field. 51792 * @param sQuery {String} Query string. 51793 * @private 51794 */ 51795YAHOO.widget.AutoComplete.prototype._typeAhead = function(elListItem, sQuery) { 51796 // Don't typeAhead if turned off or is backspace 51797 if(!this.typeAhead || (this._nKeyCode == 8)) { 51798 return; 51799 } 51800 51801 var oSelf = this, 51802 elTextbox = this._elTextbox; 51803 51804 // Only if text selection is supported 51805 if(elTextbox.setSelectionRange || elTextbox.createTextRange) { 51806 // Set and store timeout for this typeahead 51807 this._nTypeAheadDelayID = setTimeout(function() { 51808 // Select the portion of text that the user has not typed 51809 var nStart = elTextbox.value.length; // any saved queries plus what user has typed 51810 oSelf._updateValue(elListItem); 51811 var nEnd = elTextbox.value.length; 51812 oSelf._selectText(elTextbox,nStart,nEnd); 51813 var sPrefill = elTextbox.value.substr(nStart,nEnd); 51814 // Bug 2528552: Store as a selection 51815 oSelf._sCurQuery = elListItem._sResultMatch; 51816 oSelf.typeAheadEvent.fire(oSelf,sQuery,sPrefill); 51817 YAHOO.log("Typeahead occured with prefill string \"" + sPrefill + "\"", "info", oSelf.toString()); 51818 },(this.typeAheadDelay*1000)); 51819 } 51820}; 51821 51822/** 51823 * Selects text in the input field. 51824 * 51825 * @method _selectText 51826 * @param elTextbox {HTMLElement} Text input box element in which to select text. 51827 * @param nStart {Number} Starting index of text string to select. 51828 * @param nEnd {Number} Ending index of text selection. 51829 * @private 51830 */ 51831YAHOO.widget.AutoComplete.prototype._selectText = function(elTextbox, nStart, nEnd) { 51832 if(elTextbox.setSelectionRange) { // For Mozilla 51833 elTextbox.setSelectionRange(nStart,nEnd); 51834 } 51835 else if(elTextbox.createTextRange) { // For IE 51836 var oTextRange = elTextbox.createTextRange(); 51837 oTextRange.moveStart("character", nStart); 51838 oTextRange.moveEnd("character", nEnd-elTextbox.value.length); 51839 oTextRange.select(); 51840 } 51841 else { 51842 elTextbox.select(); 51843 } 51844}; 51845 51846/** 51847 * Extracts rightmost query from delimited string. 51848 * 51849 * @method _extractQuery 51850 * @param sQuery {String} String to parse 51851 * @return {Object} Object literal containing properties "query" and "previous". 51852 * @private 51853 */ 51854YAHOO.widget.AutoComplete.prototype._extractQuery = function(sQuery) { 51855 var aDelimChar = this.delimChar, 51856 nDelimIndex = -1, 51857 nNewIndex, nQueryStart, 51858 i = aDelimChar.length-1, 51859 sPrevious; 51860 51861 // Loop through all possible delimiters and find the rightmost one in the query 51862 // A " " may be a false positive if they are defined as delimiters AND 51863 // are used to separate delimited queries 51864 for(; i >= 0; i--) { 51865 nNewIndex = sQuery.lastIndexOf(aDelimChar[i]); 51866 if(nNewIndex > nDelimIndex) { 51867 nDelimIndex = nNewIndex; 51868 } 51869 } 51870 // If we think the last delimiter is a space (" "), make sure it is NOT 51871 // a false positive by also checking the char directly before it 51872 if(aDelimChar[i] == " ") { 51873 for (var j = aDelimChar.length-1; j >= 0; j--) { 51874 if(sQuery[nDelimIndex - 1] == aDelimChar[j]) { 51875 nDelimIndex--; 51876 break; 51877 } 51878 } 51879 } 51880 // A delimiter has been found in the query so extract the latest query from past selections 51881 if(nDelimIndex > -1) { 51882 nQueryStart = nDelimIndex + 1; 51883 // Trim any white space from the beginning... 51884 while(sQuery.charAt(nQueryStart) == " ") { 51885 nQueryStart += 1; 51886 } 51887 // ...and save the rest of the string for later 51888 sPrevious = sQuery.substring(0,nQueryStart); 51889 // Here is the query itself 51890 sQuery = sQuery.substr(nQueryStart); 51891 } 51892 // No delimiter found in the query, so there are no selections from past queries 51893 else { 51894 sPrevious = ""; 51895 } 51896 51897 return { 51898 previous: sPrevious, 51899 query: sQuery 51900 }; 51901}; 51902 51903/** 51904 * Syncs results container with its helpers. 51905 * 51906 * @method _toggleContainerHelpers 51907 * @param bShow {Boolean} True if container is expanded, false if collapsed 51908 * @private 51909 */ 51910YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) { 51911 var width = this._elContent.offsetWidth + "px"; 51912 var height = this._elContent.offsetHeight + "px"; 51913 51914 if(this.useIFrame && this._elIFrame) { 51915 var elIFrame = this._elIFrame; 51916 if(bShow) { 51917 elIFrame.style.width = width; 51918 elIFrame.style.height = height; 51919 elIFrame.style.padding = ""; 51920 YAHOO.log("Iframe expanded", "info", this.toString()); 51921 } 51922 else { 51923 elIFrame.style.width = 0; 51924 elIFrame.style.height = 0; 51925 elIFrame.style.padding = 0; 51926 YAHOO.log("Iframe collapsed", "info", this.toString()); 51927 } 51928 } 51929 if(this.useShadow && this._elShadow) { 51930 var elShadow = this._elShadow; 51931 if(bShow) { 51932 elShadow.style.width = width; 51933 elShadow.style.height = height; 51934 YAHOO.log("Shadow expanded", "info", this.toString()); 51935 } 51936 else { 51937 elShadow.style.width = 0; 51938 elShadow.style.height = 0; 51939 YAHOO.log("Shadow collapsed", "info", this.toString()); 51940 } 51941 } 51942}; 51943 51944/** 51945 * Animates expansion or collapse of the container. 51946 * 51947 * @method _toggleContainer 51948 * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed 51949 * @private 51950 */ 51951YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) { 51952 YAHOO.log("Toggling container " + ((bShow) ? "open" : "closed"), "info", this.toString()); 51953 51954 var elContainer = this._elContainer; 51955 51956 // If implementer has container always open and it's already open, don't mess with it 51957 // Container is initialized with display "none" so it may need to be shown first time through 51958 if(this.alwaysShowContainer && this._bContainerOpen) { 51959 return; 51960 } 51961 51962 // Reset states 51963 if(!bShow) { 51964 this._toggleHighlight(this._elCurListItem,"from"); 51965 this._nDisplayedItems = 0; 51966 this._sCurQuery = null; 51967 51968 // Container is already closed, so don't bother with changing the UI 51969 if(this._elContent.style.display == "none") { 51970 return; 51971 } 51972 } 51973 51974 // If animation is enabled... 51975 var oAnim = this._oAnim; 51976 if(oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) { 51977 if(oAnim.isAnimated()) { 51978 oAnim.stop(true); 51979 } 51980 51981 // Clone container to grab current size offscreen 51982 var oClone = this._elContent.cloneNode(true); 51983 elContainer.appendChild(oClone); 51984 oClone.style.top = "-9000px"; 51985 oClone.style.width = ""; 51986 oClone.style.height = ""; 51987 oClone.style.display = ""; 51988 51989 // Current size of the container is the EXPANDED size 51990 var wExp = oClone.offsetWidth; 51991 var hExp = oClone.offsetHeight; 51992 51993 // Calculate COLLAPSED sizes based on horiz and vert anim 51994 var wColl = (this.animHoriz) ? 0 : wExp; 51995 var hColl = (this.animVert) ? 0 : hExp; 51996 51997 // Set animation sizes 51998 oAnim.attributes = (bShow) ? 51999 {width: { to: wExp }, height: { to: hExp }} : 52000 {width: { to: wColl}, height: { to: hColl }}; 52001
52002 // If opening anew, set to a collapsed size... 52003 if(bShow && !this._bContainerOpen) { 52004 this._elContent.style.width = wColl+"px"; 52005 this._elContent.style.height = hColl+"px"; 52006 } 52007 // Else, set it to its last known size. 52008 else { 52009 this._elContent.style.width = wExp+"px"; 52010 this._elContent.style.height = hExp+"px"; 52011 } 52012 52013 elContainer.removeChild(oClone); 52014 oClone = null; 52015 52016 var oSelf = this; 52017 var onAnimComplete = function() { 52018 // Finish the collapse 52019 oAnim.onComplete.unsubscribeAll(); 52020 52021 if(bShow) { 52022 oSelf._toggleContainerHelpers(true); 52023 oSelf._bContainerOpen = bShow; 52024 oSelf.containerExpandEvent.fire(oSelf); 52025 YAHOO.log("Container expanded", "info", oSelf.toString()); 52026 } 52027 else { 52028 oSelf._elContent.style.display = "none"; 52029 oSelf._bContainerOpen = bShow; 52030 oSelf.containerCollapseEvent.fire(oSelf); 52031 YAHOO.log("Container collapsed", "info", oSelf.toString()); 52032 } 52033 }; 52034 52035 // Display container and animate it 52036 this._toggleContainerHelpers(false); // Bug 1424486: Be early to hide, late to show; 52037 this._elContent.style.display = ""; 52038 oAnim.onComplete.subscribe(onAnimComplete); 52039 oAnim.animate(); 52040 } 52041 // Else don't animate, just show or hide 52042 else { 52043 if(bShow) { 52044 this._elContent.style.display = ""; 52045 this._toggleContainerHelpers(true); 52046 this._bContainerOpen = bShow; 52047 this.containerExpandEvent.fire(this); 52048 YAHOO.log("Container expanded", "info", this.toString()); 52049 } 52050 else { 52051 this._toggleContainerHelpers(false); 52052 this._elContent.style.display = "none"; 52053 this._bContainerOpen = bShow; 52054 this.containerCollapseEvent.fire(this); 52055 YAHOO.log("Container collapsed", "info", this.toString()); 52056 } 52057 } 52058 52059}; 52060 52061/** 52062 * Toggles the highlight on or off for an item in the container, and also cleans 52063 * up highlighting of any previous item. 52064 * 52065 * @method _toggleHighlight 52066 * @param elNewListItem {HTMLElement} The <li> element item to receive highlight behavior. 52067 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off. 52068 * @private 52069 */ 52070YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(elNewListItem, sType) { 52071 if(elNewListItem) { 52072 var sHighlight = this.highlightClassName; 52073 if(this._elCurListItem) { 52074 // Remove highlight from old item 52075 YAHOO.util.Dom.removeClass(this._elCurListItem, sHighlight); 52076 this._elCurListItem = null; 52077 } 52078 52079 if((sType == "to") && sHighlight) { 52080 // Apply highlight to new item 52081 YAHOO.util.Dom.addClass(elNewListItem, sHighlight); 52082 this._elCurListItem = elNewListItem; 52083 } 52084 } 52085}; 52086 52087/** 52088 * Toggles the pre-highlight on or off for an item in the container, and also cleans 52089 * up pre-highlighting of any previous item. 52090 * 52091 * @method _togglePrehighlight 52092 * @param elNewListItem {HTMLElement} The <li> element item to receive highlight behavior. 52093 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off. 52094 * @private 52095 */ 52096YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(elNewListItem, sType) { 52097 var sPrehighlight = this.prehighlightClassName; 52098 52099 if(this._elCurPrehighlightItem) { 52100 YAHOO.util.Dom.removeClass(this._elCurPrehighlightItem, sPrehighlight); 52101 } 52102 if(elNewListItem == this._elCurListItem) { 52103 return; 52104 } 52105 52106 if((sType == "mouseover") && sPrehighlight) { 52107 // Apply prehighlight to new item 52108 YAHOO.util.Dom.addClass(elNewListItem, sPrehighlight); 52109 this._elCurPrehighlightItem = elNewListItem; 52110 } 52111 else { 52112 // Remove prehighlight from old item 52113 YAHOO.util.Dom.removeClass(elNewListItem, sPrehighlight); 52114 } 52115}; 52116 52117/** 52118 * Updates the text input box value with selected query result. If a delimiter 52119 * has been defined, then the value gets appended with the delimiter. 52120 *
52121 * @method _updateValue 52122 * @param elListItem {HTMLElement} The <li> element item with which to update the value. 52123 * @private 52124 */ 52125YAHOO.widget.AutoComplete.prototype._updateValue = function(elListItem) { 52126 if(!this.suppressInputUpdate) { 52127 var elTextbox = this._elTextbox; 52128 var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null; 52129 var sResultMatch = elListItem._sResultMatch; 52130 52131 // Calculate the new value 52132 var sNewValue = ""; 52133 if(sDelimChar) { 52134 // Preserve selections from past queries 52135 sNewValue = this._sPastSelections; 52136 // Add new selection plus delimiter 52137 sNewValue += sResultMatch + sDelimChar; 52138 if(sDelimChar != " ") { 52139 sNewValue += " "; 52140 } 52141 } 52142 else { 52143 sNewValue = sResultMatch; 52144 } 52145 52146 // Update input field 52147 elTextbox.value = sNewValue; 52148 52149 // Scroll to bottom of textarea if necessary 52150 if(elTextbox.type == "textarea") { 52151 elTextbox.scrollTop = elTextbox.scrollHeight; 52152 } 52153 52154 // Move cursor to end 52155 var end = elTextbox.value.length; 52156 this._selectText(elTextbox,end,end); 52157 52158 this._elCurListItem = elListItem; 52159 } 52160}; 52161 52162/** 52163 * Selects a result item from the container 52164 * 52165 * @method _selectItem 52166 * @param elListItem {HTMLElement} The selected <li> element item. 52167 * @private 52168 */ 52169YAHOO.widget.AutoComplete.prototype._selectItem = function(elListItem) { 52170 this._bItemSelected = true; 52171 this._updateValue(elListItem); 52172 this._sPastSelections = this._elTextbox.value; 52173 this._clearInterval(); 52174 this.itemSelectEvent.fire(this, elListItem, elListItem._oResultData); 52175 YAHOO.log("Item selected: " + YAHOO.lang.dump(elListItem._oResultData), "info", this.toString()); 52176 this._toggleContainer(false); 52177}; 52178 52179/** 52180 * If an item is highlighted in the container, the right arrow key jumps to the 52181 * end of the textbox and selects the highlighted item, otherwise the container 52182 * is closed. 52183 * 52184 * @method _jumpSelection 52185 * @private 52186 */ 52187YAHOO.widget.AutoComplete.prototype._jumpSelection = function() { 52188 if(this._elCurListItem) { 52189 this._selectItem(this._elCurListItem); 52190 } 52191 else { 52192 this._toggleContainer(false); 52193 } 52194}; 52195 52196/** 52197 * Triggered by up and down arrow keys, changes the current highlighted 52198 * <li> element item. Scrolls container if necessary. 52199 * 52200 * @method _moveSelection 52201 * @param nKeyCode {Number} Code of key pressed. 52202 * @private 52203 */ 52204YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) { 52205 if(this._bContainerOpen) { 52206 // Determine current item's id number 52207 var elCurListItem = this._elCurListItem, 52208 nCurItemIndex = -1; 52209 52210 if(elCurListItem) { 52211 nCurItemIndex = elCurListItem._nItemIndex; 52212 } 52213 52214 var nNewItemIndex = (nKeyCode == 40) ? 52215 (nCurItemIndex + 1) : (nCurItemIndex - 1); 52216 52217 // Out of bounds 52218 if(nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) { 52219 return; 52220 } 52221 52222 if(elCurListItem) { 52223 // Unhighlight current item 52224 this._toggleHighlight(elCurListItem, "from"); 52225 this.itemArrowFromEvent.fire(this, elCurListItem); 52226 YAHOO.log("Item arrowed from: " + elCurListItem._nItemIndex, "info", this.toString()); 52227 } 52228 if(nNewItemIndex == -1) { 52229 // Go back to query (remove type-ahead string) 52230 if(this.delimChar) { 52231 this._elTextbox.value = this._sPastSelections + this._sCurQuery; 52232 } 52233 else { 52234 this._elTextbox.value = this._sCurQuery; 52235 } 52236 return; 52237 } 52238 if(nNewItemIndex == -2) { 52239 // Close container 52240 this._toggleContainer(false); 52241 return; 52242 } 52243 52244 var elNewListItem = this._elList.childNodes[nNewItemIndex], 52245 52246 // Scroll the container if necessary 52247 elContent = this._elContent, 52248 sOF = YAHOO.util.Dom.getStyle(elContent,"overflow"), 52249 sOFY = YAHOO.util.Dom.getStyle(elContent,"overflowY"), 52250 scrollOn = ((sOF == "auto") || (sOF == "scroll") || (sOFY == "auto") || (sOFY == "scroll")); 52251 if(scrollOn && (nNewItemIndex > -1) && 52252 (nNewItemIndex < this._nDisplayedItems)) { 52253 // User is keying down 52254 if(nKeyCode == 40) { 52255 // Bottom of selected item is below scroll area... 52256 if((elNewListItem.offsetTop+elNewListItem.offsetHeight) > (elContent.scrollTop + elContent.offsetHeight)) { 52257 // Set bottom of scroll area to bottom of selected item 52258 elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight; 52259 } 52260 // Bottom of selected item is above scroll area... 52261 else if((elNewListItem.offsetTop+elNewListItem.offsetHeight) < el
52261Content.scrollTop) { 52262 // Set top of selected item to top of scroll area 52263 elContent.scrollTop = elNewListItem.offsetTop; 52264 52265 } 52266 } 52267 // User is keying up 52268 else { 52269 // Top of selected item is above scroll area 52270 if(elNewListItem.offsetTop < elContent.scrollTop) { 52271 // Set top of scroll area to top of selected item 52272 this._elContent.scrollTop = elNewListItem.offsetTop; 52273 } 52274 // Top of selected item is below scroll area 52275 else if(elNewListItem.offsetTop > (elContent.scrollTop + elContent.offsetHeight)) { 52276 // Set bottom of selected item to bottom of scroll area 52277 this._elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight; 52278 } 52279 } 52280 } 52281 52282 this._toggleHighlight(elNewListItem, "to"); 52283 this.itemArrowToEvent.fire(this, elNewListItem); 52284 YAHOO.log("Item arrowed to " + elNewListItem._nItemIndex, "info", this.toString()); 52285 if(this.typeAhead) { 52286 this._updateValue(elNewListItem); 52287 // Bug 2528552: Store as a selection 52288 this._sCurQuery = elNewListItem._sResultMatch; 52289 } 52290 } 52291}; 52292 52293///////////////////////////////////////////////////////////////////////////// 52294// 52295// Private event handlers 52296// 52297///////////////////////////////////////////////////////////////////////////// 52298 52299/** 52300 * Handles container mouseover events. 52301 * 52302 * @method _onContainerMouseover 52303 * @param v {HTMLEvent} The mouseover event. 52304 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52305 * @private 52306 */ 52307YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) { 52308 var elTarget = YAHOO.util.Event.getTarget(v); 52309 var elTag = elTarget.nodeName.toLowerCase(); 52310 while(elTarget && (elTag != "table")) { 52311 switch(elTag) { 52312 case "body": 52313 return; 52314 case "li": 52315 if(oSelf.prehighlightClassName) { 52316 oSelf._togglePrehighlight(elTarget,"mouseover"); 52317 } 52318 else { 52319 oSelf._toggleHighlight(elTarget,"to"); 52320 } 52321 52322 oSelf.itemMouseOverEvent.fire(oSelf, elTarget); 52323 YAHOO.log("Item moused over " + elTarget._nItemIndex, "info", oSelf.toString()); 52324 break; 52325 case "div": 52326 if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) { 52327 oSelf._bOverContainer = true; 52328 return; 52329 } 52330 break; 52331 default: 52332 break; 52333 } 52334 52335 elTarget = elTarget.parentNode; 52336 if(elTarget) { 52337 elTag = elTarget.nodeName.toLowerCase(); 52338 } 52339 } 52340}; 52341 52342/** 52343 * Handles container mouseout events. 52344 * 52345 * @method _onContainerMouseout 52346 * @param v {HTMLEvent} The mouseout event. 52347 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52348 * @private 52349 */ 52350YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) { 52351 var elTarget = YAHOO.util.Event.getTarget(v); 52352 var elTag = elTarget.nodeName.toLowerCase(); 52353 while(elTarget && (elTag != "table")) { 52354 switch(elTag) { 52355 case "body": 52356 return; 52357 case "li": 52358 if(oSelf.prehighlightClassName) { 52359 oSelf._togglePrehighlight(elTarget,"mouseout"); 52360 } 52361 else { 52362 oSelf._toggleHighlight(elTarget,"from"); 52363 } 52364 52365 oSelf.itemMouseOutEvent.fire(oSelf, elTarget); 52366 YAHOO.log("Item moused out " + elTarget._nItemIndex, "info", oSelf.toString()); 52367 break; 52368 case "ul": 52369 oSelf._toggleHighlight(oSelf._elCurListItem,"to"); 52370 break; 52371 case "div": 52372 if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) { 52373 oSelf._bOverContainer = false;
52374 return; 52375 } 52376 break; 52377 default: 52378 break; 52379 } 52380 52381 elTarget = elTarget.parentNode; 52382 if(elTarget) { 52383 elTag = elTarget.nodeName.toLowerCase(); 52384 } 52385 } 52386}; 52387 52388/** 52389 * Handles container click events. 52390 * 52391 * @method _onContainerClick 52392 * @param v {HTMLEvent} The click event. 52393 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52394 * @private 52395 */ 52396YAHOO.widget.AutoComplete.prototype._onContainerClick = function(v,oSelf) { 52397 var elTarget = YAHOO.util.Event.getTarget(v); 52398 var elTag = elTarget.nodeName.toLowerCase(); 52399 while(elTarget && (elTag != "table")) { 52400 switch(elTag) { 52401 case "body": 52402 return; 52403 case "li": 52404 // In case item has not been moused over 52405 oSelf._toggleHighlight(elTarget,"to"); 52406 oSelf._selectItem(elTarget); 52407 return; 52408 default: 52409 break; 52410 } 52411 52412 elTarget = elTarget.parentNode; 52413 if(elTarget) { 52414 elTag = elTarget.nodeName.toLowerCase(); 52415 } 52416 } 52417}; 52418 52419 52420/** 52421 * Handles container scroll events. 52422 * 52423 * @method _onContainerScroll 52424 * @param v {HTMLEvent} The scroll event. 52425 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52426 * @private 52427 */ 52428YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) { 52429 oSelf._focus(); 52430}; 52431 52432/** 52433 * Handles container resize events. 52434 * 52435 * @method _onContainerResize 52436 * @param v {HTMLEvent} The resize event. 52437 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52438 * @private 52439 */ 52440YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) { 52441 oSelf._toggleContainerHelpers(oSelf._bContainerOpen); 52442}; 52443 52444 52445/** 52446 * Handles textbox keydown events of functional keys, mainly for UI behavior. 52447 * 52448 * @method _onTextboxKeyDown 52449 * @param v {HTMLEvent} The keydown event. 52450 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52451 * @private 52452 */ 52453YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) { 52454 var nKeyCode = v.keyCode; 52455 52456 // Clear timeout 52457 if(oSelf._nTypeAheadDelayID != -1) { 52458 clearTimeout(oSelf._nTypeAheadDelayID); 52459 } 52460 52461 switch (nKeyCode) { 52462 case 9: // tab 52463 if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) { 52464 // select an item or clear out 52465 if(oSelf._elCurListItem) { 52466 if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) { 52467 if(oSelf._bContainerOpen) { 52468 YAHOO.util.Event.stopEvent(v); 52469 } 52470 } 52471 oSelf._selectItem(oSelf._elCurListItem); 52472 } 52473 else { 52474 oSelf._toggleContainer(false); 52475 } 52476 } 52477 break; 52478 case 13: // enter 52479 if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) { 52480 if(oSelf._elCurListItem) { 52481 if(oSelf._nKeyCode != nKeyCode) { 52482 if(oSelf._bContainerOpen) { 52483 YAHOO.util.Event.stopEvent(v); 52484 } 52485 } 52486 oSelf._selectItem(oSelf._elCurListItem); 52487 } 52488 else { 52489 oSelf._toggleContainer(false); 52490 } 52491 } 52492 break; 52493 case 27: // esc 52494 oSelf._toggleContainer(false); 52495 return; 52496 case 39: // right 52497 oSelf._jumpSelection(); 52498 break; 52499 case 38: // up 52500 if(oSelf._bContainerOpen) { 52501 YAHOO.util.Event.stopEvent(v); 52502 oSelf._moveSelection(nKeyCode); 52503 } 52504 break; 52505 case 40: // down 52506 if(oSelf._bContainerOpen) { 52507 YAHOO.util.Event.stopEvent(v); 52508 oSelf._moveSelection(nKeyCode); 52509 } 52510 break; 52511 default: 52512 oSelf._bItemSelected = false;
52513 oSelf._toggleHighlight(oSelf._elCurListItem, "from"); 52514 52515 oSelf.textboxKeyEvent.fire(oSelf, nKeyCode); 52516 YAHOO.log("Textbox keyed", "info", oSelf.toString()); 52517 break; 52518 } 52519 52520 if(nKeyCode === 18){ 52521 oSelf._enableIntervalDetection(); 52522 } 52523 oSelf._nKeyCode = nKeyCode; 52524}; 52525 52526/** 52527 * Handles textbox keypress events. 52528 * @method _onTextboxKeyPress 52529 * @param v {HTMLEvent} The keypress event. 52530 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52531 * @private 52532 */ 52533YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) { 52534 var nKeyCode = v.keyCode; 52535 52536 // Expose only to non SF3 (bug 1978549) Mac browsers (bug 790337) and Opera browsers (bug 583531), 52537 // where stopEvent is ineffective on keydown events 52538 if(YAHOO.env.ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") != -1) && (YAHOO.env.ua.webkit < 420)) { 52539 switch (nKeyCode) { 52540 case 9: // tab 52541 // select an item or clear out 52542 if(oSelf._bContainerOpen) { 52543 if(oSelf.delimChar) { 52544 YAHOO.util.Event.stopEvent(v); 52545 } 52546 if(oSelf._elCurListItem) { 52547 oSelf._selectItem(oSelf._elCurListItem); 52548 } 52549 else { 52550 oSelf._toggleContainer(false); 52551 } 52552 } 52553 break; 52554 case 13: // enter 52555 if(oSelf._bContainerOpen) { 52556 YAHOO.util.Event.stopEvent(v); 52557 if(oSelf._elCurListItem) { 52558 oSelf._selectItem(oSelf._elCurListItem); 52559 } 52560 else { 52561 oSelf._toggleContainer(false); 52562 } 52563 } 52564 break; 52565 default: 52566 break; 52567 } 52568 } 52569 52570 //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948) 52571 // Korean IME detected 52572 else if(nKeyCode == 229) { 52573 oSelf._enableIntervalDetection(); 52574 } 52575}; 52576 52577/** 52578 * Handles textbox keyup events to trigger queries. 52579 * 52580 * @method _onTextboxKeyUp 52581 * @param v {HTMLEvent} The keyup event. 52582 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52583 * @private 52584 */ 52585YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) { 52586 var sText = this.value; //string in textbox 52587 52588 // Check to see if any of the public properties have been updated 52589 oSelf._initProps(); 52590 52591 // Filter out chars that don't trigger queries 52592 var nKeyCode = v.keyCode; 52593 if(oSelf._isIgnoreKey(nKeyCode)) { 52594 return; 52595 } 52596 52597 // Clear previous timeout 52598 if(oSelf._nDelayID != -1) { 52599 clearTimeout(oSelf._nDelayID); 52600 } 52601 52602 // Set new timeout 52603 oSelf._nDelayID = setTimeout(function(){ 52604 oSelf._sendQuery(sText); 52605 },(oSelf.queryDelay * 1000)); 52606}; 52607 52608/** 52609 * Handles text input box receiving focus. 52610 * 52611 * @method _onTextboxFocus 52612 * @param v {HTMLEvent} The focus event. 52613 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52614 * @private 52615 */ 52616YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) { 52617 // Start of a new interaction 52618 if(!oSelf._bFocused) { 52619 oSelf._elTextbox.setAttribute("autocomplete","off"); 52620 oSelf._bFocused = true; 52621 oSelf._sInitInputValue = oSelf._elTextbox.value; 52622 oSelf.textboxFocusEvent.fire(oSelf); 52623 YAHOO.log("Textbox focused", "info", oSelf.toString()); 52624 } 52625}; 52626 52627/** 52628 * Handles text input box losing focus. 52629 * 52630 * @method _onTextboxBlur 52631 * @param v {HTMLEvent} The focus event. 52632 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52633 * @private 52634 */ 52635YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) { 52636 // Is a true blur 52637 if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) { 52638 // Current query needs to be validated as a selection 52639 if(!oSelf._bItemSelected) { 52640 var elMatchListItem = oSelf._textMatchesOption(); 52641 // Container is closed or current query doesn't match any result 52642 if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && (elMatchListItem === null))) { 52643 // Force selection is enabled so clear the current query 52644 if(oSelf.forceSelection) { 52645 oSelf._clearSelection(); 52646 } 52647 // Treat current query as a valid selection 52648 else { 52649 oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery); 52650 YAHOO.log("Unmatched item selected: " + oSelf._sCurQuery, "info", oSelf.toString()); 52651 } 52652 } 52653 // Container is open and current query matches a result 52654 else { 52655 // Force a selection when textbox is blurred with a match 52656 if(oSelf.forceSelection) { 52657 oSelf._selectItem(elMatchListItem); 52658 } 52659 } 52660 } 52661 52662 oSelf._clearInterval(); 52663 oSelf._bFocused = false;
52664 if(oSelf._sInitInputValue !== oSelf._elTextbox.value) { 52665 oSelf.textboxChangeEvent.fire(oSelf); 52666 } 52667 oSelf.textboxBlurEvent.fire(oSelf); 52668 YAHOO.log("Textbox blurred", "info", oSelf.toString()); 52669 52670 oSelf._toggleContainer(false); 52671 } 52672 // Not a true blur if it was a selection via mouse click 52673 else { 52674 oSelf._focus(); 52675 } 52676}; 52677 52678/** 52679 * Handles window unload event. 52680 * 52681 * @method _onWindowUnload 52682 * @param v {HTMLEvent} The unload event. 52683 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance. 52684 * @private 52685 */ 52686YAHOO.widget.AutoComplete.prototype._onWindowUnload = function(v,oSelf) { 52687 if(oSelf && oSelf._elTextbox && oSelf.allowBrowserAutocomplete) { 52688 oSelf._elTextbox.setAttribute("autocomplete","on"); 52689 } 52690}; 52691 52692///////////////////////////////////////////////////////////////////////////// 52693// 52694// Deprecated for Backwards Compatibility 52695// 52696///////////////////////////////////////////////////////////////////////////// 52697/** 52698 * @method doBeforeSendQuery 52699 * @deprecated Use generateRequest. 52700 */ 52701YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery = function(sQuery) { 52702 return this.generateRequest(sQuery); 52703}; 52704 52705/** 52706 * @method getListItems 52707 * @deprecated Use getListEl().childNodes. 52708 */ 52709YAHOO.widget.AutoComplete.prototype.getListItems = function() { 52710 var allListItemEls = [], 52711 els = this._elList.childNodes; 52712 for(var i=els.length-1; i>=0; i--) { 52713 allListItemEls[i] = els[i]; 52714 } 52715 return allListItemEls; 52716}; 52717 52718///////////////////////////////////////////////////////////////////////// 52719// 52720// Private static methods 52721// 52722///////////////////////////////////////////////////////////////////////// 52723 52724/** 52725 * Clones object literal or array of object literals. 52726 * 52727 * @method AutoComplete._cloneObject 52728 * @param o {Object} Object. 52729 * @private 52730 * @static 52731 */ 52732YAHOO.widget.AutoComplete._cloneObject = function(o) { 52733 if(!YAHOO.lang.isValue(o)) { 52734 return o; 52735 } 52736 52737 var copy = {}; 52738 52739 if(YAHOO.lang.isFunction(o)) { 52740 copy = o; 52741 } 52742 else if(YAHOO.lang.isArray(o)) { 52743 var array = []; 52744 for(var i=0,len=o.length;i<len;i++) { 52745 array[i] = YAHOO.widget.AutoComplete._cloneObject(o[i]); 52746 } 52747 copy = array; 52748 } 52749 else if(YAHOO.lang.isObject(o)) { 52750 for (var x in o){ 52751 if(YAHOO.lang.hasOwnProperty(o, x)) { 52752 if(YAHOO.lang.isValue(o[x]) && YAHOO.lang.isObject(o[x]) || YAHOO.lang.isArray(o[x])) { 52753 copy[x] = YAHOO.widget.AutoComplete._cloneObject(o[x]); 52754 } 52755 else { 52756 copy[x] = o[x]; 52757 } 52758 } 52759 } 52760 } 52761 else { 52762 copy = o; 52763 } 52764 52765 return copy; 52766}; 52767 52768 52769 52770 52771YAHOO.register("autocomplete", YAHOO.widget.AutoComplete, {version: "2.9.0", build: "2800"}); 52772/* 52773Copyright (c) 2011, Yahoo! Inc. All rights reserved. 52774Code licensed under the BSD License: 52775http://developer.yahoo.com/yui/license.html 52776version: 2.9.0 52777*/ 52778(function () { 52779/** 52780 * The Paginator widget provides a set of controls to navigate through paged 52781 * data. 52782 * 52783 * @module paginator 52784 * @uses YAHOO.util.EventProvider 52785 * @uses YAHOO.util.AttributeProvider 52786 */ 52787 52788var Dom = YAHOO.util.Dom, 52789 lang = YAHOO.lang, 52790 isObject = lang.isObject, 52791 isFunction = lang.isFunction, 52792 isArray = lang.isArray, 52793 isString = lang.isString; 52794 52795/** 52796 * Instantiate a Paginator, passing a configuration object to the contructor. 52797 * The configuration object should contain the following properties: 52798 * <ul> 52799 * <li>rowsPerPage : <em>n</em> (int)</li> 52800 * <li>totalRecords : <em>n</em> (int or Paginator.VALUE_UNLIMITED)</li> 52801 * <li>containers : <em>id | el | arr</em> (HTMLElement reference, its id, or an array of either)</li> 52802 * </ul> 52803 * 52804 * @namespace YAHOO.widget 52805 * @class Paginator 52806 * @constructor 52807 * @param config {Object} Object literal to set instance and ui component 52808 * configuration. 52809 */ 52810function Paginator(config) { 52811 var UNLIMITED = Paginator.VALUE_UNLIMITED, 52812 attrib, initialPage, records, perPage, startIndex; 52813 52814 config = isObject(config) ? config : {}; 52815 52816 this.initConfig(); 52817 52818 this.initEvents(); 52819 52820 // Set the basic config keys first 52821 this.set('rowsPerPage',config.rowsPerPage,true); 52822 if (Paginator.isNumeric(config.totalRecords)) { 52823 this.set('totalRecords',config.totalRecords,true); 52824 } 52825 52826 this.initUIComponents(); 52827 52828 // Update the other config values 52829 for (attrib in config) { 52830 if (config.hasOwnProperty(attrib)) { 52831 this.set(attrib,config[attrib],true); 52832 } 52833 } 52834 52835 // Calculate the initial record offset 52836 initialPage = this.get('initialPage'); 52837 records = this.get('totalRecords'); 52838 perPage = this.get('rowsPerPage'); 52839 if (initialPage > 1 && perPage !== UNLIMITED) { 52840 startIndex = (initialPage - 1) * perPage; 52841 if (records === UNLIMITED || startIndex < records) { 52842 this.set('recordOffset',startIndex,true); 52843 } 52844 } 52845} 52846 52847 52848// Static members 52849lang.augmentObject(Paginator, { 52850 /** 52851 * Incrementing index used to give instances unique ids. 52852 * @static 52853 * @property Paginator.id 52854 * @type number 52855 * @private 52856 */ 52857 id : 0, 52858 52859 /** 52860 * Base of id strings used for ui components. 52861 * @static 52862 * @property Paginator.ID_BASE 52863 * @type string 52864 * @private 52865 */ 52866 ID_BASE : 'yui-pg', 52867 52868 /** 52869 * Used to identify unset, optional configurations, or used explicitly in 52870 * the case of totalRecords to indicate unlimited pagination. 52871 * @static 52872 * @property Paginator.VALUE_UNLIMITED 52873 * @type number 52874 * @final 52875 */ 52876 VALUE_UNLIMITED : -1, 52877 52878 /** 52879 * Default template used by Paginator instances. Update this if you want 52880 * all new Paginators to use a different default template. 52881 * @static 52882 * @property Paginator.TEMPLATE_DEFAULT 52883 * @type string 52884 */ 52885 TEMPLATE_DEFAULT : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}", 52886 52887 /** 52888 * Common alternate pagination format, including page links, links for 52889 * previous, next, first and last pages as well as a rows-per-page 52890 * dropdown. Offered as a convenience. 52891 * @static 52892 * @property Paginator.TEMPLATE_ROWS_PER_PAGE 52893 * @type string 52894 */ 52895 TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}", 52896 52897 /** 52898 * Storage object for UI Components 52899 * @static 52900 * @property Paginator.ui 52901 */ 52902 ui : {}, 52903 52904 /** 52905 * Similar to YAHOO.lang.isNumber, but allows numeric strings. This is
52906 * is used for attribute validation in conjunction with getters that return 52907 * numbers. 52908 * 52909 * @method Paginator.isNumeric 52910 * @param v {Number|String} value to be checked for number or numeric string 52911 * @returns {Boolean} true if the input is coercable into a finite number 52912 * @static 52913 */ 52914 isNumeric : function (v) { 52915 return isFinite(+v); 52916 }, 52917 52918 /** 52919 * Return a number or null from input 52920 * 52921 * @method Paginator.toNumber 52922 * @param n {Number|String} a number or numeric string 52923 * @return Number 52924 * @static 52925 */ 52926 toNumber : function (n) { 52927 return isFinite(+n) ? +n : null; 52928 } 52929 52930},true); 52931 52932 52933// Instance members and methods 52934Paginator.prototype = { 52935 52936 // Instance members 52937 52938 /** 52939 * Array of nodes in which to render pagination controls. This is set via 52940 * the "containers" attribute. 52941 * @property _containers 52942 * @type Array(HTMLElement) 52943 * @private 52944 */ 52945 _containers : [], 52946 52947 /** 52948 * Flag used to indicate multiple attributes are being updated via setState 52949 * @property _batch 52950 * @type boolean 52951 * @protected 52952 */ 52953 _batch : false, 52954 52955 /** 52956 * Used by setState to indicate when a page change has occurred 52957 * @property _pageChanged 52958 * @type boolean 52959 * @protected 52960 */ 52961 _pageChanged : false, 52962 52963 /** 52964 * Temporary state cache used by setState to keep track of the previous 52965 * state for eventual pageChange event firing 52966 * @property _state 52967 * @type Object 52968 * @protected 52969 */ 52970 _state : null, 52971 52972 52973 // Instance methods 52974 52975 /** 52976 * Initialize the Paginator's attributes (see YAHOO.util.Element class 52977 * AttributeProvider). 52978 * @method initConfig 52979 * @private 52980 */ 52981 initConfig : function () { 52982 52983 var UNLIMITED = Paginator.VALUE_UNLIMITED; 52984 52985 /** 52986 * REQUIRED. Number of records constituting a "page" 52987 * @attribute rowsPerPage 52988 * @type integer 52989 */ 52990 this.setAttributeConfig('rowsPerPage', { 52991 value : 0, 52992 validator : Paginator.isNumeric, 52993 setter : Paginator.toNumber 52994 }); 52995 52996 /** 52997 * REQUIRED. Node references or ids of nodes in which to render the 52998 * pagination controls. 52999 * @attribute containers 53000 * @type {string|HTMLElement|Array(string|HTMLElement)} 53001 */ 53002 this.setAttributeConfig('containers', { 53003 value : null, 53004 validator : function (val) { 53005 if (!isArray(val)) { 53006 val = [val]; 53007 } 53008 for (var i = 0, len = val.length; i < len; ++i) { 53009 if (isString(val[i]) || 53010 (isObject(val[i]) && val[i].nodeType === 1)) { 53011 continue; 53012 } 53013 return false; 53014 } 53015 return true; 53016 }, 53017 method : function (val) { 53018 val = Dom.get(val); 53019 if (!isArray(val)) { 53020 val = [val]; 53021 } 53022 this._containers = val; 53023 } 53024 }); 53025 53026 /** 53027 * Total number of records to paginate through 53028 * @attribute totalRecords 53029 * @type integer 53030 * @default 0 53031 */ 53032 this.setAttributeConfig('totalRecords', { 53033 value : 0, 53034 validator : Paginator.isNumeric, 53035 setter : Paginator.toNumber 53036 }); 53037 53038 /** 53039 * Zero based index of the record considered first on the current page. 53040 * For page based interactions, don't modify this attribute directly; 53041 * use setPage(n). 53042 * @attribute recordOffset 53043 * @type integer 53044 * @default 0 53045 */ 53046 this.setAttributeConfig('recordOffset', { 53047 value : 0, 53048 validator : function (val) { 53049 var total = this.get('totalRecords'); 53050 if (Paginator.isNumeric(val)) { 53051 val = +val; 53052 return total === UNLIMITED || total > val || 53053 (total === 0 && val === 0); 53054 } 53055 53056 return false;
53057 }, 53058 setter : Paginator.toNumber 53059 }); 53060 53061 /** 53062 * Page to display on initial paint 53063 * @attribute initialPage 53064 * @type integer 53065 * @default 1 53066 */ 53067 this.setAttributeConfig('initialPage', { 53068 value : 1, 53069 validator : Paginator.isNumeric, 53070 setter : Paginator.toNumber 53071 }); 53072 53073 /** 53074 * Template used to render controls. The string will be used as 53075 * innerHTML on all specified container nodes. Bracketed keys 53076 * (e.g. {pageLinks}) in the string will be replaced with an instance 53077 * of the so named ui component. 53078 * @see Paginator.TEMPLATE_DEFAULT 53079 * @see Paginator.TEMPLATE_ROWS_PER_PAGE 53080 * @attribute template 53081 * @type string 53082 */ 53083 this.setAttributeConfig('template', { 53084 value : Paginator.TEMPLATE_DEFAULT, 53085 validator : isString 53086 }); 53087 53088 /** 53089 * Class assigned to the element(s) containing pagination controls. 53090 * @attribute containerClass 53091 * @type string 53092 * @default 'yui-pg-container' 53093 */ 53094 this.setAttributeConfig('containerClass', { 53095 value : 'yui-pg-container', 53096 validator : isString 53097 }); 53098 53099 /** 53100 * Display pagination controls even when there is only one page. Set 53101 * to false to forgo rendering and/or hide the containers when there 53102 * is only one page of data. Note if you are using the rowsPerPage 53103 * dropdown ui component, visibility will be maintained as long as the 53104 * number of records exceeds the smallest page size. 53105 * @attribute alwaysVisible 53106 * @type boolean 53107 * @default true 53108 */ 53109 this.setAttributeConfig('alwaysVisible', { 53110 value : true, 53111 validator : lang.isBoolean 53112 }); 53113 53114 /** 53115 * Update the UI immediately upon interaction. If false, changeRequest 53116 * subscribers or other external code will need to explicitly set the 53117 * new values in the paginator to trigger repaint. 53118 * @attribute updateOnChange 53119 * @type boolean 53120 * @default false 53121 * @deprecated use changeRequest listener that calls setState 53122 */ 53123 this.setAttributeConfig('updateOnChange', { 53124 value : false, 53125 validator : lang.isBoolean 53126 }); 53127 53128 53129 53130 // Read only attributes 53131 53132 /** 53133 * Unique id assigned to this instance 53134 * @attribute id 53135 * @type integer 53136 * @final 53137 */ 53138 this.setAttributeConfig('id', { 53139 value : Paginator.id++, 53140 readOnly : true 53141 }); 53142 53143 /** 53144 * Indicator of whether the DOM nodes have been initially created 53145 * @attribute rendered 53146 * @type boolean 53147 * @final 53148 */ 53149 this.setAttributeConfig('rendered', { 53150 value : false, 53151 readOnly : true 53152 }); 53153 53154 }, 53155 53156 /** 53157 * Initialize registered ui components onto this instance. 53158 * @method initUIComponents 53159 * @private 53160 */ 53161 initUIComponents : function () { 53162 var ui = Paginator.ui, 53163 name,UIComp; 53164 for (name in ui) { 53165 if (ui.hasOwnProperty(name)) { 53166 UIComp = ui[name]; 53167 if (isObject(UIComp) && isFunction(UIComp.init)) { 53168 UIComp.init(this); 53169 } 53170 } 53171 } 53172 }, 53173 53174 /** 53175 * Initialize this instance's CustomEvents. 53176 * @method initEvents 53177 * @private 53178 */ 53179 initEvents : function () { 53180 /** 53181 * Event fired when the Paginator is initially rendered 53182 * @event render 53183 */ 53184 this.createEvent('render'); 53185 53186 /** 53187 * Event fired when the Paginator is initially rendered 53188 * @event rendered 53189 * @deprecated use render event 53190 */ 53191 this.createEvent('rendered'); // backward compatibility 53192 53193 /** 53194 * Event fired when a change in pagination values is requested, 53195 * either by interacting with the various ui components or via the 53196 * setStartIndex(n) etc APIs. 53197 * Subscribers will receive the proposed state as the first parameter. 53198 * The proposed state object will contain the following keys: 53199 * <ul> 53200 * <li>paginator - the Paginator instance</li> 53201 * <li>page</li> 53202 * <li>totalRecords</li> 53203 * <li>recordOffset - index of the first record on the new page</li> 53204 * <li>rowsPerPage</li> 53205 * <li>records - array containing [start index, end index] for the records on the new page</li> 53206 * <li>before - object literal with all these keys for the current state</li> 53207 * </ul> 53208 * @event changeRequest 53209 */ 53210 this.createEvent('changeRequest'); 53211 53212 /** 53213 * Event fired when attribute changes have resulted in the calculated 53214 * current page changing. 53215 * @event pageChange 53216 */ 53217 this.createEvent('pageChange'); 53218 53219 /** 53220 * Event that fires before the destroy event. 53221 * @event beforeDestroy 53222 */ 53223 this.createEvent('beforeDestroy'); 53224 53225 /** 53226 * Event used to trigger cleanup of ui components 53227 * @event destroy 53228 */ 53229 this.createEvent('destroy'); 53230 53231 this._selfSubscribe(); 53232 }, 53233 53234 /** 53235 * Subscribes to instance attribute change events to automate certain 53236 * behaviors. 53237 * @method _selfSubscribe 53238 * @protected 53239 */ 53240 _selfSubscribe : function () { 53241 // Listen for changes to totalRecords and alwaysVisible 53242 this.subscribe('totalRecordsChange',this.updateVisibility,this,true); 53243 this.subscribe('alwaysVisibleChange',this.updateVisibility,this,true); 53244 53245 // Fire the pageChange event when appropriate 53246 this.subscribe('totalRecordsChange',this._handleStateChange,this,true); 53247 this.subscribe('recordOffsetChange',this._handleStateChange,this,true); 53248 this.subscribe('rowsPerPageChange',this._handleStateChange,this,true); 53249 53250 // Update recordOffset when totalRecords is reduced below 53251 this.subscribe('totalRecordsChange',this._syncRecordOffset,this,true); 53252 }, 53253 53254 /** 53255 * Sets recordOffset to the starting index of the previous page when 53256 * totalRecords is reduced below the current recordOffset. 53257 * @method _syncRecordOffset 53258 * @param e {Event} totalRecordsChange event 53259 * @protected 53260 */ 53261 _syncRecordOffset : function (e) { 53262 var v = e.newValue,rpp,state; 53263 if (e.prevValue !== v) { 53264 if (v !== Paginator.VALUE_UNLIMITED) { 53265 rpp = this.get('rowsPerPage'); 53266 53267 if (rpp && this.get('recordOffset') >= v) {
53268 state = this.getState({ 53269 totalRecords : e.prevValue, 53270 recordOffset : this.get('recordOffset') 53271 }); 53272 53273 this.set('recordOffset', state.before.recordOffset); 53274 this._firePageChange(state); 53275 } 53276 } 53277 } 53278 }, 53279 53280 /** 53281 * Fires the pageChange event when the state attributes have changed in 53282 * such a way as to locate the current recordOffset on a new page. 53283 * @method _handleStateChange 53284 * @param e {Event} the attribute change event 53285 * @protected 53286 */ 53287 _handleStateChange : function (e) { 53288 if (e.prevValue !== e.newValue) { 53289 var change = this._state || {}, 53290 state; 53291 53292 change[e.type.replace(/Change$/,'')] = e.prevValue; 53293 state = this.getState(change); 53294 53295 if (state.page !== state.before.page) { 53296 if (this._batch) { 53297 this._pageChanged = true; 53298 } else { 53299 this._firePageChange(state); 53300 } 53301 } 53302 } 53303 }, 53304 53305 /** 53306 * Fires a pageChange event in the form of a standard attribute change 53307 * event with additional properties prevState and newState. 53308 * @method _firePageChange 53309 * @param state {Object} the result of getState(oldState) 53310 * @protected 53311 */ 53312 _firePageChange : function (state) { 53313 if (isObject(state)) { 53314 var current = state.before; 53315 delete state.before; 53316 this.fireEvent('pageChange',{ 53317 type : 'pageChange', 53318 prevValue : state.page, 53319 newValue : current.page, 53320 prevState : state, 53321 newState : current 53322 }); 53323 } 53324 }, 53325 53326 /** 53327 * Render the pagination controls per the format attribute into the 53328 * specified container nodes. 53329 * @method render 53330 * @return the Paginator instance 53331 * @chainable 53332 */ 53333 render : function () { 53334 if (this.get('rendered')) { 53335 return this; 53336 } 53337 53338 var template = this.get('template'), 53339 state = this.getState(), 53340 // ex. yui-pg0-1 (first paginator, second container) 53341 id_base = Paginator.ID_BASE + this.get('id') + '-', 53342 i, len; 53343 53344 // Assemble the containers, keeping them hidden 53345 for (i = 0, len = this._containers.length; i < len; ++i) { 53346 this._renderTemplate(this._containers[i],template,id_base+i,true); 53347 } 53348 53349 // Show the containers if appropriate 53350 this.updateVisibility(); 53351 53352 // Set render attribute manually to support its readOnly contract 53353 if (this._containers.length) { 53354 this.setAttributeConfig('rendered', { value: true }); 53355 53356 this.fireEvent('render', state); 53357 // For backward compatibility 53358 this.fireEvent('rendered', state); 53359 } 53360 53361 return this; 53362 }, 53363 53364 /** 53365 * Creates the individual ui components and renders them into a container. 53366 * 53367 * @method _renderTemplate 53368 * @param container {HTMLElement} where to add the ui components 53369 * @param template {String} the template to use as a guide for rendering 53370 * @param id_base {String} id base for the container's ui components 53371 * @param hide {Boolean} leave the container hidden after assembly 53372 * @protected 53373 */ 53374 _renderTemplate : function (container, template, id_base, hide) { 53375 var containerClass = this.get('containerClass'), 53376 markers, i, len; 53377 53378 if (!container) { 53379 return; 53380 } 53381 53382 // Hide the container while its contents are rendered 53383 Dom.setStyle(container,'display','none'); 53384 53385 Dom.addClass(container, containerClass); 53386 53387 // Place the template innerHTML, adding marker spans to the template 53388 // html to indicate drop zones for ui components 53389 container.innerHTML = template.replace(/\{([a-z0-9_ \-]+)\}/gi, 53390 '<span class="yui-pg-ui yui-pg-ui-$1"></span>'); 53391 53392 // Replace each marker with the ui component's render() output 53393 markers = Dom.getElementsByClassName('yui-pg-ui','span',container); 53394 53395 for (i = 0, len = markers.length; i < len; ++i) { 53396 this.renderUIComponent(markers[i], id_base); 53397 } 53398 53399 if (!hide) { 53400 // Show the container allowing page reflow 53401 Dom.setStyle(container,'display',''); 53402 } 53403 }, 53404 53405 /** 53406 * Replaces a marker node with a rendered UI component, determined by the 53407 * yui-pg-ui-(UI component class name) in the marker's className. e.g. 53408 * yui-pg-ui-PageLinks => new YAHOO.widget.Paginator.ui.PageLinks(this) 53409 * 53410 * @method renderUIComponent 53411 * @param marker {HTMLElement} the marker node to replace 53412 * @param id_base {String} string base the component's generated id 53413 * @return the Paginator instance 53414 * @chainable 53415 */ 53416 renderUIComponent : function (marker, id_base) { 53417 var par = marker.parentNode, 53418 name = /yui-pg-ui-(\w+)/.exec(marker.className), 53419 UIComp = name && Paginator.ui[name[1]], 53420 comp; 53421 53422 if (isFunction(UIComp)) { 53423 comp = new UIComp(this); 53424 if (isFunction(comp.render)) { 53425 par.replaceChild(comp.render(id_base),marker); 53426 } 53427 } 53428 53429 return this; 53430 }, 53431 53432 /** 53433 * Removes controls from the page and unhooks events. 53434 * @method destroy 53435 */ 53436 destroy : function () { 53437 this.fireEvent('beforeDestroy'); 53438 this.fireEvent('destroy'); 53439 53440 this.setAttributeConfig('rendered',{value:false}); 53441 this.unsubscribeAll(); 53442 }, 53443 53444 /** 53445 * Hides the containers if there is only one page of data and attribute 53446 * alwaysVisible is false. Conversely, it displays the containers if either 53447 * there is more than one page worth of data or alwaysVisible is turned on. 53448 * @method updateVisibility 53449 */ 53450 updateVisibility : function (e) { 53451 var alwaysVisible = this.get('alwaysVisible'), 53452 totalRecords, visible, rpp, rppOptions, i, len, opt; 53453 53454 if (!e || e.type === 'alwaysVisibleChange' || !alwaysVisible) { 53455 totalRecords = this.get('totalRecords'); 53456 visible = true; 53457 rpp = this.get('rowsPerPage'); 53458 rppOptions = this.get('rowsPerPageOptions'); 53459 53460 if (isArray(rppOptions)) { 53461 for (i = 0, len = rppOptions.length; i < len; ++i) { 53462 opt = rppOptions[i]; 53463 // account for value 'all' 53464 if (lang.isNumber(opt || opt.value)) { 53465 rpp = Math.min(rpp, (opt.value || opt)); 53466 } 53467 } 53468 } 53469 53470 if (totalRecords !== Paginator.VALUE_UNLIMITED && 53471 totalRecords <= rpp) { 53472 visible = false;
53473 } 53474 53475 visible = visible || alwaysVisible; 53476 53477 for (i = 0, len = this._containers.length; i < len; ++i) { 53478 Dom.setStyle(this._containers[i],'display', 53479 visible ? '' : 'none'); 53480 } 53481 } 53482 }, 53483 53484 53485 53486 53487 /** 53488 * Get the configured container nodes 53489 * @method getContainerNodes 53490 * @return {Array} array of HTMLElement nodes 53491 */ 53492 getContainerNodes : function () { 53493 return this._containers; 53494 }, 53495 53496 /** 53497 * Get the total number of pages in the data set according to the current 53498 * rowsPerPage and totalRecords values. If totalRecords is not set, or 53499 * set to YAHOO.widget.Paginator.VALUE_UNLIMITED, returns 53500 * YAHOO.widget.Paginator.VALUE_UNLIMITED. 53501 * @method getTotalPages 53502 * @return {number} 53503 */ 53504 getTotalPages : function () { 53505 var records = this.get('totalRecords'), 53506 perPage = this.get('rowsPerPage'); 53507 53508 // rowsPerPage not set. Can't calculate 53509 if (!perPage) { 53510 return null; 53511 } 53512 53513 if (records === Paginator.VALUE_UNLIMITED) { 53514 return Paginator.VALUE_UNLIMITED; 53515 } 53516 53517 return Math.ceil(records/perPage); 53518 }, 53519 53520 /** 53521 * Does the requested page have any records? 53522 * @method hasPage 53523 * @param page {number} the page in question 53524 * @return {boolean} 53525 */ 53526 hasPage : function (page) { 53527 if (!lang.isNumber(page) || page < 1) { 53528 return false; 53529 } 53530 53531 var totalPages = this.getTotalPages(); 53532 53533 return (totalPages === Paginator.VALUE_UNLIMITED || totalPages >= page); 53534 }, 53535 53536 /** 53537 * Get the page number corresponding to the current record offset. 53538 * @method getCurrentPage 53539 * @return {number} 53540 */ 53541 getCurrentPage : function () { 53542 var perPage = this.get('rowsPerPage'); 53543 if (!perPage || !this.get('totalRecords')) { 53544 return 0; 53545 } 53546 return Math.floor(this.get('recordOffset') / perPage) + 1; 53547 }, 53548 53549 /** 53550 * Are there records on the next page? 53551 * @method hasNextPage 53552 * @return {boolean} 53553 */ 53554 hasNextPage : function () { 53555 var currentPage = this.getCurrentPage(), 53556 totalPages = this.getTotalPages(); 53557 53558 return currentPage && (totalPages === Paginator.VALUE_UNLIMITED || currentPage < totalPages); 53559 }, 53560 53561 /** 53562 * Get the page number of the next page, or null if the current page is the 53563 * last page. 53564 * @method getNextPage 53565 * @return {number} 53566 */ 53567 getNextPage : function () { 53568 return this.hasNextPage() ? this.getCurrentPage() + 1 : null; 53569 }, 53570 53571 /** 53572 * Is there a page before the current page? 53573 * @method hasPreviousPage 53574 * @return {boolean} 53575 */ 53576 hasPreviousPage : function () { 53577 return (this.getCurrentPage() > 1); 53578 }, 53579 53580 /** 53581 * Get the page number of the previous page, or null if the current page 53582 * is the first page. 53583 * @method getPreviousPage 53584 * @return {number} 53585 */ 53586 getPreviousPage : function () { 53587 return (this.hasPreviousPage() ? this.getCurrentPage() - 1 : 1); 53588 }, 53589 53590 /** 53591 * Get the start and end record indexes of the specified page. 53592 * @method getPageRecords 53593 * @param page {number} (optional) The page (current page if not specified) 53594 * @return {Array} [start_index, end_index] 53595 */ 53596 getPageRecords : function (page) { 53597 if (!lang.isNumber(page)) { 53598 page = this.getCurrentPage(); 53599 } 53600 53601 var perPage = this.get('rowsPerPage'), 53602 records = this.get('totalRecords'), 53603 start, end; 53604 53605 if (!page || !perPage) { 53606 return null; 53607 } 53608 53609 start = (page - 1) * perPage; 53610 if (records !== Paginator.VALUE_UNLIMITED) { 53611 if (start >= records) { 53612 return null; 53613 } 53614 end = Math.min(start + perPage, records) - 1; 53615 } else { 53616 end = start + perPage - 1; 53617 } 53618 53619 return [start,end]; 53620 }, 53621 53622 /** 53623 * Set the current page to the provided page number if possible. 53624 * @method setPage 53625 * @param newPage {number} the new page number 53626 * @param silent {boolean} whether to forcibly avoid firing the 53627 * changeRequest event 53628 */ 53629 setPage : function (page,silent) { 53630 if (this.hasPage(page) && page !== this.getCurrentPage()) { 53631 if (this.get('updateOnChange') || silent) { 53632 this.set('recordOffset', (page - 1) * this.get('rowsPerPage')); 53633 } else {
53634 this.fireEvent('changeRequest',this.getState({'page':page})); 53635 } 53636 } 53637 }, 53638 53639 /** 53640 * Get the number of rows per page. 53641 * @method getRowsPerPage 53642 * @return {number} the current setting of the rowsPerPage attribute 53643 */ 53644 getRowsPerPage : function () { 53645 return this.get('rowsPerPage'); 53646 }, 53647 53648 /** 53649 * Set the number of rows per page. 53650 * @method setRowsPerPage 53651 * @param rpp {number} the new number of rows per page 53652 * @param silent {boolean} whether to forcibly avoid firing the 53653 * changeRequest event 53654 */ 53655 setRowsPerPage : function (rpp,silent) { 53656 if (Paginator.isNumeric(rpp) && +rpp > 0 && 53657 +rpp !== this.get('rowsPerPage')) { 53658 if (this.get('updateOnChange') || silent) { 53659 this.set('rowsPerPage',rpp); 53660 } else { 53661 this.fireEvent('changeRequest', 53662 this.getState({'rowsPerPage':+rpp})); 53663 } 53664 } 53665 }, 53666 53667 /** 53668 * Get the total number of records. 53669 * @method getTotalRecords 53670 * @return {number} the current setting of totalRecords attribute 53671 */ 53672 getTotalRecords : function () { 53673 return this.get('totalRecords'); 53674 }, 53675 53676 /** 53677 * Set the total number of records. 53678 * @method setTotalRecords 53679 * @param total {number} the new total number of records 53680 * @param silent {boolean} whether to forcibly avoid firing the changeRequest event 53681 */ 53682 setTotalRecords : function (total,silent) { 53683 if (Paginator.isNumeric(total) && +total >= 0 && 53684 +total !== this.get('totalRecords')) { 53685 if (this.get('updateOnChange') || silent) { 53686 this.set('totalRecords',total); 53687 } else { 53688 this.fireEvent('changeRequest',
53689 this.getState({'totalRecords':+total})); 53690 } 53691 } 53692 }, 53693 53694 /** 53695 * Get the index of the first record on the current page 53696 * @method getStartIndex 53697 * @return {number} the index of the first record on the current page 53698 */ 53699 getStartIndex : function () { 53700 return this.get('recordOffset'); 53701 }, 53702 53703 /** 53704 * Move the record offset to a new starting index. This will likely cause 53705 * the calculated current page to change. You should probably use setPage. 53706 * @method setStartIndex 53707 * @param offset {number} the new record offset 53708 * @param silent {boolean} whether to forcibly avoid firing the changeRequest event 53709 */ 53710 setStartIndex : function (offset,silent) { 53711 if (Paginator.isNumeric(offset) && +offset >= 0 && 53712 +offset !== this.get('recordOffset')) { 53713 if (this.get('updateOnChange') || silent) { 53714 this.set('recordOffset',offset); 53715 } else { 53716 this.fireEvent('changeRequest',
53717 this.getState({'recordOffset':+offset})); 53718 } 53719 } 53720 }, 53721 53722 /** 53723 * Get an object literal describing the current state of the paginator. If 53724 * an object literal of proposed values is passed, the proposed state will 53725 * be returned as an object literal with the following keys: 53726 * <ul> 53727 * <li>paginator - instance of the Paginator</li> 53728 * <li>page - number</li> 53729 * <li>totalRecords - number</li> 53730 * <li>recordOffset - number</li> 53731 * <li>rowsPerPage - number</li> 53732 * <li>records - [ start_index, end_index ]</li> 53733 * <li>before - (OPTIONAL) { state object literal for current state }</li> 53734 * </ul> 53735 * @method getState 53736 * @return {object} 53737 * @param changes {object} OPTIONAL object literal with proposed values 53738 * Supported change keys include: 53739 * <ul> 53740 * <li>rowsPerPage</li> 53741 * <li>totalRecords</li> 53742 * <li>recordOffset OR</li> 53743 * <li>page</li> 53744 * </ul> 53745 */ 53746 getState : function (changes) { 53747 var UNLIMITED = Paginator.VALUE_UNLIMITED, 53748 M = Math, max = M.max, ceil = M.ceil, 53749 currentState, state, offset; 53750 53751 function normalizeOffset(offset,total,rpp) { 53752 if (offset <= 0 || total === 0) { 53753 return 0; 53754 } 53755 if (total === UNLIMITED || total > offset) { 53756 return offset - (offset % rpp); 53757 } 53758 return total - (total % rpp || rpp); 53759 } 53760 53761 currentState = { 53762 paginator : this, 53763 totalRecords : this.get('totalRecords'), 53764 rowsPerPage : this.get('rowsPerPage'), 53765 records : this.getPageRecords() 53766 }; 53767 currentState.recordOffset = normalizeOffset( 53768 this.get('recordOffset'), 53769 currentState.totalRecords, 53770 currentState.rowsPerPage); 53771 currentState.page = ceil(currentState.recordOffset / 53772 currentState.rowsPerPage) + 1; 53773 53774 if (!changes) { 53775 return currentState; 53776 } 53777 53778 state = { 53779 paginator : this, 53780 before : currentState, 53781 53782 rowsPerPage : changes.rowsPerPage || currentState.rowsPerPage, 53783 totalRecords : (Paginator.isNumeric(changes.totalRecords) ? 53784 max(changes.totalRecords,UNLIMITED) : 53785 +currentState.totalRecords) 53786 }; 53787 53788 if (state.totalRecords === 0) { 53789 state.recordOffset = 53790 state.page = 0; 53791 } else { 53792 offset = Paginator.isNumeric(changes.page) ? 53793 (changes.page - 1) * state.rowsPerPage : 53794 Paginator.isNumeric(changes.recordOffset) ? 53795 +changes.recordOffset : 53796 currentState.recordOffset; 53797 53798 state.recordOffset = normalizeOffset(offset, 53799 state.totalRecords, 53800 state.rowsPerPage); 53801 53802 state.page = ceil(state.recordOffset / state.rowsPerPage) + 1; 53803 } 53804 53805 state.records = [ state.recordOffset, 53806 state.recordOffset + state.rowsPerPage - 1 ]; 53807 53808 // limit upper index to totalRecords - 1 53809 if (state.totalRecords !== UNLIMITED && 53810 state.recordOffset < state.totalRecords && state.records && 53811 state.records[1] > state.totalRecords - 1) { 53812 state.records[1] = state.totalRecords - 1; 53813 } 53814 53815 return state; 53816 }, 53817 53818 /** 53819 * Convenience method to facilitate setting state attributes rowsPerPage, 53820 * totalRecords, recordOffset in batch. Also supports calculating 53821 * recordOffset from state.page if state.recordOffset is not provided. 53822 * Fires only a single pageChange event, if appropriate. 53823 * This will not fire a changeRequest event. 53824 * @method setState 53825 * @param state {Object} Object literal of attribute:value pairs to set 53826 */ 53827 setState : function (state) { 53828 if (isObject(state)) { 53829 // get flux state based on current state with before state as well
53830 this._state = this.getState({}); 53831 53832 // use just the state props from the input obj 53833 state = { 53834 page : state.page, 53835 rowsPerPage : state.rowsPerPage, 53836 totalRecords : state.totalRecords, 53837 recordOffset : state.recordOffset 53838 }; 53839 53840 // calculate recordOffset from page if recordOffset not specified. 53841 // not using lang.isNumber for support of numeric strings 53842 if (state.page && state.recordOffset === undefined) { 53843 state.recordOffset = (state.page - 1) * 53844 (state.rowsPerPage || this.get('rowsPerPage')); 53845 } 53846 53847 this._batch = true; 53848 this._pageChanged = false; 53849 53850 for (var k in state) { 53851 if (state.hasOwnProperty(k) && this._configs.hasOwnProperty(k)) { 53852 this.set(k,state[k]); 53853 } 53854 } 53855 53856 this._batch = false; 53857 53858 if (this._pageChanged) { 53859 this._pageChanged = false; 53860 53861 this._firePageChange(this.getState(this._state)); 53862 } 53863 } 53864 } 53865}; 53866 53867lang.augmentProto(Paginator, YAHOO.util.AttributeProvider); 53868 53869YAHOO.widget.Paginator = Paginator; 53870})(); 53871(function () { 53872 53873var Paginator = YAHOO.widget.Paginator, 53874 l = YAHOO.lang, 53875 setId = YAHOO.util.Dom.generateId; 53876 53877/** 53878 * ui Component to generate the textual report of current pagination status. 53879 * E.g. "Now viewing page 1 of 13". 53880 * 53881 * @namespace YAHOO.widget.Paginator.ui 53882 * @class CurrentPageReport 53883 * @for YAHOO.widget.Paginator 53884 * 53885 * @constructor 53886 * @param p {Pagintor} Paginator instance to attach to 53887 */ 53888Paginator.ui.CurrentPageReport = function (p) { 53889 this.paginator = p; 53890 53891 p.subscribe('recordOffsetChange', this.update,this,true); 53892 p.subscribe('rowsPerPageChange', this.update,this,true); 53893 p.subscribe('totalRecordsChange',this.update,this,true); 53894 p.subscribe('pageReportTemplateChange', this.update,this,true); 53895 p.subscribe('destroy',this.destroy,this,true); 53896 53897 //TODO: make this work 53898 p.subscribe('pageReportClassChange', this.update,this,true); 53899}; 53900 53901/** 53902 * Decorates Paginator instances with new attributes. Called during 53903 * Paginator instantiation. 53904 * @method init 53905 * @param p {Paginator} Paginator instance to decorate 53906 * @static 53907 */ 53908Paginator.ui.CurrentPageReport.init = function (p) { 53909 53910 /** 53911 * CSS class assigned to the span containing the info. 53912 * @attribute pageReportClass 53913 * @default 'yui-pg-current' 53914 */ 53915 p.setAttributeConfig('pageReportClass', { 53916 value : 'yui-pg-current', 53917 validator : l.isString 53918 }); 53919 53920 /** 53921 * Used as innerHTML for the span. Place holders in the form of {name} 53922 * will be replaced with the so named value from the key:value map 53923 * generated by the function held in the pageReportValueGenerator attribute. 53924 * @attribute pageReportTemplate 53925 * @default '({currentPage} of {totalPages})' 53926 * @see pageReportValueGenerator attribute 53927 */ 53928 p.setAttributeConfig('pageReportTemplate', { 53929 value : '({currentPage} of {totalPages})', 53930 validator : l.isString 53931 }); 53932 53933 /** 53934 * Function to generate the value map used to populate the 53935 * pageReportTemplate. The function is passed the Paginator instance as a 53936 * parameter. The default function returns a map with the following keys: 53937 * <ul> 53938 * <li>currentPage</li> 53939 * <li>totalPages</li> 53940 * <li>startIndex</li> 53941 * <li>endIndex</li> 53942 * <li>startRecord</li> 53943 * <li>endRecord</li> 53944 * <li>totalRecords</li> 53945 * </ul> 53946 * @attribute pageReportValueGenarator 53947 */ 53948 p.setAttributeConfig('pageReportValueGenerator', { 53949 value : function (paginator) { 53950 var curPage = paginator.getCurrentPage(), 53951 records = paginator.getPageRecords(); 53952 53953 return { 53954 'currentPage' : records ? curPage : 0, 53955 'totalPages' : paginator.getTotalPages(), 53956 'startIndex' : records ? records[0] : 0, 53957 'endIndex' : records ? records[1] : 0, 53958 'startRecord' : records ? records[0] + 1 : 0, 53959 'endRecord' : records ? records[1] + 1 : 0, 53960 'totalRecords': paginator.get('totalRecords') 53961 }; 53962 }, 53963 validator : l.isFunction 53964 }); 53965}; 53966 53967/** 53968 * Replace place holders in a string with the named values found in an 53969 * object literal. 53970 * @static 53971 * @method sprintf 53972 * @param template {string} The content string containing place holders 53973 * @param values {object} The key:value pairs used to replace the place holders 53974 * @return {string} 53975 */ 53976Paginator.ui.CurrentPageReport.sprintf = function (template, values) { 53977 return template.replace(/\{([\w\s\-]+)\}/g, function (x,key) { 53978 return (key in values) ? values[key] : ''; 53979 }); 53980}; 53981 53982Paginator.ui.CurrentPageReport.prototype = { 53983 53984 /** 53985 * Span node containing the formatted info 53986 * @property span 53987 * @type HTMLElement 53988 * @private 53989 */ 53990 span : null, 53991 53992 53993 /** 53994 * Generate the span containing info formatted per the pageReportTemplate 53995 * attribute. 53996 * @method render 53997 * @param id_base {string} used to create unique ids for generated nodes 53998 * @return {HTMLElement} 53999 */ 54000 render : function (id_base) { 54001 this.span = document.createElement('span'); 54002 this.span.className = this.paginator.get('pageReportClass'); 54003 setId(this.span, id_base + '-page-report'); 54004 this.update(); 54005 54006 return this.span; 54007 }, 54008 54009 /** 54010 * Regenerate the content of the span if appropriate. Calls 54011 * CurrentPageReport.sprintf with the value of the pageReportTemplate 54012 * attribute and the value map returned from pageReportValueGenerator 54013 * function. 54014 * @method update 54015 * @param e {CustomEvent} The calling change event 54016 */ 54017 update : function (e) {
54018 if (e && e.prevValue === e.newValue) { 54019 return; 54020 } 54021 54022 this.span.innerHTML = Paginator.ui.CurrentPageReport.sprintf( 54023 this.paginator.get('pageReportTemplate'), 54024 this.paginator.get('pageReportValueGenerator')(this.paginator)); 54025 }, 54026 54027 /** 54028 * Removes the link/span node and clears event listeners 54029 * removal. 54030 * @method destroy 54031 * @private 54032 */ 54033 destroy : function () { 54034 this.span.parentNode.removeChild(this.span); 54035 this.span = null; 54036 } 54037 54038}; 54039 54040})(); 54041(function () { 54042 54043var Paginator = YAHOO.widget.Paginator, 54044 l = YAHOO.lang, 54045 setId = YAHOO.util.Dom.generateId; 54046 54047/** 54048 * ui Component to generate the page links 54049 * 54050 * @namespace YAHOO.widget.Paginator.ui 54051 * @class PageLinks 54052 * @for YAHOO.widget.Paginator 54053 * 54054 * @constructor 54055 * @param p {Pagintor} Paginator instance to attach to 54056 */ 54057Paginator.ui.PageLinks = function (p) { 54058 this.paginator = p; 54059 54060 p.subscribe('recordOffsetChange',this.update,this,true); 54061 p.subscribe('rowsPerPageChange',this.update,this,true); 54062 p.subscribe('totalRecordsChange',this.update,this,true); 54063 p.subscribe('pageLinksChange', this.rebuild,this,true); 54064 p.subscribe('pageLinkClassChange', this.rebuild,this,true); 54065 p.subscribe('currentPageClassChange', this.rebuild,this,true); 54066 p.subscribe('destroy',this.destroy,this,true); 54067 54068 //TODO: Make this work 54069 p.subscribe('pageLinksContainerClassChange', this.rebuild,this,true); 54070}; 54071 54072/** 54073 * Decorates Paginator instances with new attributes. Called during 54074 * Paginator instantiation. 54075 * @method init 54076 * @param p {Paginator} Paginator instance to decorate 54077 * @static 54078 */ 54079Paginator.ui.PageLinks.init = function (p) { 54080 54081 /** 54082 * CSS class assigned to each page link/span. 54083 * @attribute pageLinkClass 54084 * @default 'yui-pg-page' 54085 */ 54086 p.setAttributeConfig('pageLinkClass', { 54087 value : 'yui-pg-page', 54088 validator : l.isString 54089 }); 54090 54091 /** 54092 * CSS class assigned to the current page span. 54093 * @attribute currentPageClass 54094 * @default 'yui-pg-current-page' 54095 */ 54096 p.setAttributeConfig('currentPageClass', { 54097 value : 'yui-pg-current-page', 54098 validator : l.isString 54099 }); 54100 54101 /** 54102 * CSS class assigned to the span containing the page links. 54103 * @attribute pageLinksContainerClass 54104 * @default 'yui-pg-pages' 54105 */ 54106 p.setAttributeConfig('pageLinksContainerClass', { 54107 value : 'yui-pg-pages', 54108 validator : l.isString 54109 }); 54110 54111 /** 54112 * Maximum number of page links to display at one time. 54113 * @attribute pageLinks 54114 * @default 10 54115 */ 54116 p.setAttributeConfig('pageLinks', { 54117 value : 10, 54118 validator : Paginator.isNumeric 54119 }); 54120 54121 /** 54122 * Function used generate the innerHTML for each page link/span. The 54123 * function receives as parameters the page number and a reference to the 54124 * paginator object. 54125 * @attribute pageLabelBuilder 54126 * @default function (page, paginator) { return page; } 54127 */ 54128 p.setAttributeConfig('pageLabelBuilder', { 54129 value : function (page, paginator) { return page; }, 54130 validator : l.isFunction 54131 }); 54132 54133 /** 54134 * Function used generate the title for each page link. The 54135 * function receives as parameters the page number and a reference to the 54136 * paginator object. 54137 * @attribute pageTitleBuilder 54138 * @default function (page, paginator) { return page; } 54139 */ 54140 p.setAttributeConfig('pageTitleBuilder', { 54141 value : function (page, paginator) { return "Page " + page; }, 54142 validator : l.isFunction 54143 }); 54144}; 54145 54146/** 54147 * Calculates start and end page numbers given a current page, attempting 54148 * to keep the current page in the middle 54149 * @static 54150 * @method calculateRange 54151 * @param {int} currentPage The current page 54152 * @param {int} totalPages (optional) Maximum number of pages 54153 * @param {int} numPages (optional) Preferred number of pages in range 54154 * @return {Array} [start_page_number, end_page_number] 54155 */ 54156Paginator.ui.PageLinks.calculateRange = function (currentPage,totalPages,numPages) { 54157 var UNLIMITED = Paginator.VALUE_UNLIMITED, 54158 start, end, delta; 54159 54160 // Either has no pages, or unlimited pages. Show none. 54161 if (!currentPage || numPages === 0 || totalPages === 0 || 54162 (totalPages === UNLIMITED && numPages === UNLIMITED)) { 54163 return [0,-1]; 54164 } 54165 54166 // Limit requested pageLinks if there are fewer totalPages 54167 if (totalPages !== UNLIMITED) { 54168 numPages = numPages === UNLIMITED ? 54169 totalPages : 54170 Math.min(numPages,totalPages); 54171 } 54172 54173 // Determine start and end, trying to keep current in the middle 54174 start = Math.max(1,Math.ceil(currentPage - (numPages/2))); 54175 if (totalPages === UNLIMITED) { 54176 end = start + numPages - 1; 54177 } else { 54178 end = Math.min(totalPages, start + numPages - 1); 54179 } 54180 54181 // Adjust the start index when approaching the last page 54182 delta = numPages - (end - start + 1); 54183 start = Math.max(1, start - delta); 54184 54185 return [start,end]; 54186}; 54187 54188 54189Paginator.ui.PageLinks.prototype = { 54190 54191 /** 54192 * Current page 54193 * @property current 54194 * @type number 54195 * @private 54196 */ 54197 current : 0, 54198 54199 /** 54200 * Span node containing the page links 54201 * @property container 54202 * @type HTMLElement 54203 * @private 54204 */ 54205 container : null, 54206 54207 54208 /** 54209 * Generate the nodes and return the container node containing page links 54210 * appropriate to the current pagination state. 54211 * @method render 54212 * @param id_base {string} used to create unique ids for generated nodes 54213 * @return {HTMLElement} 54214 */ 54215 render : function (id_base) { 54216 var p = this.paginator; 54217 54218 // Set up container 54219 this.container = document.createElement('span'); 54220 setId(this.container, id_base + '-pages'); 54221 this.container.className = p.get('pageLinksContainerClass'); 54222 YAHOO.util.Event.on(this.container,'click',this.onClick,this,true); 54223 54224 // Call update, flagging a need to rebuild 54225 this.update({newValue : null, rebuild : true}); 54226 54227 return this.container; 54228 }, 54229 54230 /** 54231 * Update the links if appropriate 54232 * @method update 54233 * @param e {CustomEvent} The calling change event 54234 */ 54235 update : function (e) {
54236 if (e && e.prevValue === e.newValue) { 54237 return; 54238 } 54239 54240 var p = this.paginator, 54241 currentPage = p.getCurrentPage(); 54242 54243 // Replace content if there's been a change 54244 if (this.current !== currentPage || !currentPage || e.rebuild) { 54245 var labelBuilder = p.get('pageLabelBuilder'), 54246 titleBuilder = p.get('pageTitleBuilder'), 54247 range = Paginator.ui.PageLinks.calculateRange( 54248 currentPage, 54249 p.getTotalPages(), 54250 p.get('pageLinks')), 54251 start = range[0], 54252 end = range[1], 54253 content = '', 54254 linkTemplate,i,spanTemplate; 54255 54256 linkTemplate = '<a href="#" class="{class}" page="{page}" title="{title}">{label}</a>'; 54257 spanTemplate = '<span class="{class}">{label}</span>'; 54258 for (i = start; i <= end; ++i) { 54259 54260 if (i === currentPage) { 54261 content += l.substitute(spanTemplate, { 54262 'class' : p.get('currentPageClass') + ' ' + p.get('pageLinkClass'), 54263 'label' : labelBuilder(i,p) 54264 }); 54265 54266 } else { 54267 content += l.substitute(linkTemplate, { 54268 'class' : p.get('pageLinkClass'), 54269 'page' : i, 54270 'label' : labelBuilder(i,p), 54271 'title' : titleBuilder(i,p) 54272 }); 54273 } 54274 } 54275 54276 this.container.innerHTML = content; 54277 } 54278 }, 54279 54280 /** 54281 * Force a rebuild of the page links. 54282 * @method rebuild 54283 * @param e {CustomEvent} The calling change event 54284 */ 54285 rebuild : function (e) { 54286 e.rebuild = true; 54287 this.update(e); 54288 }, 54289 54290 /** 54291 * Removes the page links container node and clears event listeners 54292 * @method destroy 54293 * @private 54294 */ 54295 destroy : function () { 54296 YAHOO.util.Event.purgeElement(this.container,true); 54297 this.container.parentNode.removeChild(this.container); 54298 this.container = null; 54299 }, 54300 54301 /** 54302 * Listener for the container's onclick event. Looks for qualifying link 54303 * clicks, and pulls the page number from the link's page attribute. 54304 * Sends link's page attribute to the Paginator's setPage method. 54305 * @method onClick 54306 * @param e {DOMEvent} The click event 54307 */ 54308 onClick : function (e) { 54309 var t = YAHOO.util.Event.getTarget(e); 54310 if (t && YAHOO.util.Dom.hasClass(t, 54311 this.paginator.get('pageLinkClass'))) { 54312 54313 YAHOO.util.Event.stopEvent(e); 54314 54315 this.paginator.setPage(parseInt(t.getAttribute('page'),10)); 54316 } 54317 } 54318 54319}; 54320 54321})(); 54322(function () { 54323 54324var Paginator = YAHOO.widget.Paginator, 54325 l = YAHOO.lang, 54326 setId = YAHOO.util.Dom.generateId; 54327 54328/** 54329 * ui Component to generate the link to jump to the first page. 54330 * 54331 * @namespace YAHOO.widget.Paginator.ui 54332 * @class FirstPageLink 54333 * @for YAHOO.widget.Paginator 54334 * 54335 * @constructor 54336 * @param p {Pagintor} Paginator instance to attach to 54337 */ 54338Paginator.ui.FirstPageLink = function (p) { 54339 this.paginator = p; 54340 54341 p.subscribe('recordOffsetChange',this.update,this,true); 54342 p.subscribe('rowsPerPageChange',this.update,this,true); 54343 p.subscribe('totalRecordsChange',this.update,this,true); 54344 p.subscribe('destroy',this.destroy,this,true); 54345 54346 // TODO: make this work 54347 p.subscribe('firstPageLinkLabelChange',this.update,this,true); 54348 p.subscribe('firstPageLinkClassChange',this.update,this,true); 54349}; 54350 54351/** 54352 * Decorates Paginator instances with new attributes. Called during 54353 * Paginator instantiation. 54354 * @method init 54355 * @param p {Paginator} Paginator instance to decorate 54356 * @static 54357 */ 54358Paginator.ui.FirstPageLink.init = function (p) { 54359 54360 /** 54361 * Used as innerHTML for the first page link/span. 54362 * @attribute firstPageLinkLabel 54363 * @default '<< first' 54364 */ 54365 p.setAttributeConfig('firstPageLinkLabel', { 54366 value : '<< first', 54367 validator : l.isString 54368 }); 54369 54370 /** 54371 * CSS class assigned to the link/span 54372 * @attribute firstPageLinkClass 54373 * @default 'yui-pg-first' 54374 */ 54375 p.setAttributeConfig('firstPageLinkClass', { 54376 value : 'yui-pg-first', 54377 validator : l.isString 54378 }); 54379 54380 /** 54381 * Used as title for the first page link. 54382 * @attribute firstPageLinkTitle 54383 * @default 'First Page' 54384 */ 54385 p.setAttributeConfig('firstPageLinkTitle', { 54386 value : 'First Page', 54387 validator : l.isString 54388 }); 54389}; 54390 54391// Instance members and methods 54392Paginator.ui.FirstPageLink.prototype = { 54393 54394 /** 54395 * The currently placed HTMLElement node 54396 * @property current 54397 * @type HTMLElement 54398 * @private 54399 */ 54400 current : null, 54401 54402 /** 54403 * Link node 54404 * @property link 54405 * @type HTMLElement 54406 * @private 54407 */ 54408 link : null, 54409 54410 /** 54411 * Span node (inactive link) 54412 * @property span 54413 * @type HTMLElement 54414 * @private 54415 */ 54416 span : null, 54417 54418 /** 54419 * Generate the nodes and return the appropriate node given the current 54420 * pagination state. 54421 * @method render 54422 * @param id_base {string} used to create unique ids for generated nodes 54423 * @return {HTMLElement} 54424 */ 54425 render : function (id_base) { 54426 var p = this.paginator, 54427 c = p.get('firstPageLinkClass'),
54428 label = p.get('firstPageLinkLabel'), 54429 title = p.get('firstPageLinkTitle'); 54430 54431 this.link = document.createElement('a'); 54432 this.span = document.createElement('span'); 54433 54434 setId(this.link, id_base + '-first-link'); 54435 this.link.href = '#'; 54436 this.link.className = c; 54437 this.link.innerHTML = label; 54438 this.link.title = title; 54439 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); 54440 54441 setId(this.span, id_base + '-first-span'); 54442 this.span.className = c; 54443 this.span.innerHTML = label; 54444 54445 this.current = p.getCurrentPage() > 1 ? this.link : this.span; 54446 return this.current; 54447 }, 54448 54449 /** 54450 * Swap the link and span nodes if appropriate. 54451 * @method update 54452 * @param e {CustomEvent} The calling change event 54453 */ 54454 update : function (e) { 54455 if (e && e.prevValue === e.newValue) { 54456 return; 54457 } 54458 54459 var par = this.current ? this.current.parentNode : null; 54460 if (this.paginator.getCurrentPage() > 1) { 54461 if (par && this.current === this.span) { 54462 par.replaceChild(this.link,this.current); 54463 this.current = this.link; 54464 } 54465 } else { 54466 if (par && this.current === this.link) { 54467 par.replaceChild(this.span,this.current); 54468 this.current = this.span; 54469 } 54470 } 54471 }, 54472 54473 /** 54474 * Removes the link/span node and clears event listeners 54475 * removal. 54476 * @method destroy 54477 * @private 54478 */ 54479 destroy : function () { 54480 YAHOO.util.Event.purgeElement(this.link); 54481 this.current.parentNode.removeChild(this.current); 54482 this.link = this.span = null; 54483 }, 54484 54485 /** 54486 * Listener for the link's onclick event. Pass new value to setPage method. 54487 * @method onClick 54488 * @param e {DOMEvent} The click event 54489 */ 54490 onClick : function (e) { 54491 YAHOO.util.Event.stopEvent(e); 54492 this.paginator.setPage(1); 54493 } 54494}; 54495 54496})(); 54497(function () { 54498 54499var Paginator = YAHOO.widget.Paginator, 54500 l = YAHOO.lang, 54501 setId = YAHOO.util.Dom.generateId; 54502 54503/** 54504 * ui Component to generate the link to jump to the last page. 54505 * 54506 * @namespace YAHOO.widget.Paginator.ui 54507 * @class LastPageLink 54508 * @for YAHOO.widget.Paginator 54509 * 54510 * @constructor 54511 * @param p {Pagintor} Paginator instance to attach to 54512 */ 54513Paginator.ui.LastPageLink = function (p) { 54514 this.paginator = p; 54515 54516 p.subscribe('recordOffsetChange',this.update,this,true); 54517 p.subscribe('rowsPerPageChange',this.update,this,true); 54518 p.subscribe('totalRecordsChange',this.update,this,true); 54519 p.subscribe('destroy',this.destroy,this,true); 54520 54521 // TODO: make this work 54522 p.subscribe('lastPageLinkLabelChange',this.update,this,true); 54523 p.subscribe('lastPageLinkClassChange', this.update,this,true); 54524}; 54525 54526/** 54527 * Decorates Paginator instances with new attributes. Called during 54528 * Paginator instantiation. 54529 * @method init 54530 * @param paginator {Paginator} Paginator instance to decorate 54531 * @static 54532 */ 54533Paginator.ui.LastPageLink.init = function (p) { 54534 54535 /** 54536 * Used as innerHTML for the last page link/span. 54537 * @attribute lastPageLinkLabel 54538 * @default 'last >>' 54539 */ 54540 p.setAttributeConfig('lastPageLinkLabel', { 54541 value : 'last >>', 54542 validator : l.isString 54543 }); 54544 54545 /** 54546 * CSS class assigned to the link/span 54547 * @attribute lastPageLinkClass 54548 * @default 'yui-pg-last' 54549 */ 54550 p.setAttributeConfig('lastPageLinkClass', { 54551 value : 'yui-pg-last', 54552 validator : l.isString 54553 }); 54554 54555 /** 54556 * Used as title for the last page link. 54557 * @attribute lastPageLinkTitle 54558 * @default 'Last Page' 54559 */ 54560 p.setAttributeConfig('lastPageLinkTitle', { 54561 value : 'Last Page', 54562 validator : l.isString 54563 }); 54564 54565}; 54566 54567Paginator.ui.LastPageLink.prototype = { 54568 54569 /** 54570 * Currently placed HTMLElement node 54571 * @property current 54572 * @type HTMLElement 54573 * @private 54574 */ 54575 current : null, 54576 54577 /** 54578 * Link HTMLElement node 54579 * @property link 54580 * @type HTMLElement 54581 * @private 54582 */ 54583 link : null, 54584 54585 /** 54586 * Span node (inactive link) 54587 * @property span 54588 * @type HTMLElement 54589 * @private 54590 */ 54591 span : null, 54592 54593 /** 54594 * Empty place holder node for when the last page link is inappropriate to 54595 * display in any form (unlimited paging). 54596 * @property na 54597 * @type HTMLElement 54598 * @private 54599 */ 54600 na : null, 54601 54602 54603 /** 54604 * Generate the nodes and return the appropriate node given the current 54605 * pagination state. 54606 * @method render 54607 * @param id_base {string} used to create unique ids for generated nodes 54608 * @return {HTMLElement} 54609 */ 54610 render : function (id_base) { 54611 var p = this.paginator, 54612 c = p.get('lastPageLinkClass'),
54613 label = p.get('lastPageLinkLabel'), 54614 last = p.getTotalPages(), 54615 title = p.get('lastPageLinkTitle'); 54616 54617 this.link = document.createElement('a'); 54618 this.span = document.createElement('span'); 54619 this.na = this.span.cloneNode(false); 54620 54621 setId(this.link, id_base + '-last-link'); 54622 this.link.href = '#'; 54623 this.link.className = c; 54624 this.link.innerHTML = label; 54625 this.link.title = title; 54626 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); 54627 54628 setId(this.span, id_base + '-last-span'); 54629 this.span.className = c; 54630 this.span.innerHTML = label; 54631 54632 setId(this.na, id_base + '-last-na'); 54633 54634 switch (last) { 54635 case Paginator.VALUE_UNLIMITED : 54636 this.current = this.na; break; 54637 case p.getCurrentPage() : 54638 this.current = this.span; break; 54639 default : 54640 this.current = this.link; 54641 } 54642 54643 return this.current; 54644 }, 54645 54646 /** 54647 * Swap the link, span, and na nodes if appropriate. 54648 * @method update 54649 * @param e {CustomEvent} The calling change event (ignored) 54650 */ 54651 update : function (e) { 54652 if (e && e.prevValue === e.newValue) { 54653 return; 54654 } 54655 54656 var par = this.current ? this.current.parentNode : null, 54657 after = this.link; 54658 54659 if (par) { 54660 switch (this.paginator.getTotalPages()) { 54661 case Paginator.VALUE_UNLIMITED : 54662 after = this.na; break; 54663 case this.paginator.getCurrentPage() : 54664 after = this.span; break; 54665 } 54666 54667 if (this.current !== after) { 54668 par.replaceChild(after,this.current); 54669 this.current = after; 54670 } 54671 } 54672 }, 54673 54674 /** 54675 * Removes the link/span node and clears event listeners 54676 * @method destroy 54677 * @private 54678 */ 54679 destroy : function () { 54680 YAHOO.util.Event.purgeElement(this.link); 54681 this.current.parentNode.removeChild(this.current); 54682 this.link = this.span = null; 54683 }, 54684 54685 /** 54686 * Listener for the link's onclick event. Passes to setPage method. 54687 * @method onClick 54688 * @param e {DOMEvent} The click event 54689 */ 54690 onClick : function (e) { 54691 YAHOO.util.Event.stopEvent(e); 54692 this.paginator.setPage(this.paginator.getTotalPages()); 54693 } 54694}; 54695 54696})(); 54697(function () { 54698 54699var Paginator = YAHOO.widget.Paginator, 54700 l = YAHOO.lang, 54701 setId = YAHOO.util.Dom.generateId; 54702 54703/** 54704 * ui Component to generate the link to jump to the next page. 54705 * 54706 * @namespace YAHOO.widget.Paginator.ui 54707 * @class NextPageLink 54708 * @for YAHOO.widget.Paginator 54709 * 54710 * @constructor 54711 * @param p {Pagintor} Paginator instance to attach to 54712 */ 54713Paginator.ui.NextPageLink = function (p) { 54714 this.paginator = p; 54715 54716 p.subscribe('recordOffsetChange', this.update,this,true); 54717 p.subscribe('rowsPerPageChange', this.update,this,true); 54718 p.subscribe('totalRecordsChange', this.update,this,true); 54719 p.subscribe('destroy',this.destroy,this,true); 54720 54721 // TODO: make this work 54722 p.subscribe('nextPageLinkLabelChange', this.update,this,true); 54723 p.subscribe('nextPageLinkClassChange', this.update,this,true); 54724}; 54725 54726/** 54727 * Decorates Paginator instances with new attributes. Called during 54728 * Paginator instantiation. 54729 * @method init 54730 * @param p {Paginator} Paginator instance to decorate 54731 * @static 54732 */ 54733Paginator.ui.NextPageLink.init = function (p) { 54734 54735 /** 54736 * Used as innerHTML for the next page link/span. 54737 * @attribute nextPageLinkLabel 54738 * @default 'next >' 54739 */ 54740 p.setAttributeConfig('nextPageLinkLabel', { 54741 value : 'next >', 54742 validator : l.isString 54743 }); 54744 54745 /** 54746 * CSS class assigned to the link/span 54747 * @attribute nextPageLinkClass 54748 * @default 'yui-pg-next' 54749 */ 54750 p.setAttributeConfig('nextPageLinkClass', { 54751 value : 'yui-pg-next', 54752 validator : l.isString 54753 }); 54754 54755 /** 54756 * Used as title for the next page link. 54757 * @attribute nextPageLinkTitle 54758 * @default 'Next Page' 54759 */ 54760 p.setAttributeConfig('nextPageLinkTitle', { 54761 value : 'Next Page', 54762 validator : l.isString 54763 }); 54764 54765}; 54766 54767Paginator.ui.NextPageLink.prototype = { 54768 54769 /** 54770 * Currently placed HTMLElement node 54771 * @property current 54772 * @type HTMLElement 54773 * @private 54774 */ 54775 current : null, 54776 54777 /** 54778 * Link node 54779 * @property link 54780 * @type HTMLElement 54781 * @private 54782 */ 54783 link : null, 54784 54785 /** 54786 * Span node (inactive link) 54787 * @property span 54788 * @type HTMLElement 54789 * @private 54790 */ 54791 span : null, 54792 54793 54794 /** 54795 * Generate the nodes and return the appropriate node given the current 54796 * pagination state. 54797 * @method render 54798 * @param id_base {string} used to create unique ids for generated nodes 54799 * @return {HTMLElement} 54800 */ 54801 render : function (id_base) { 54802 var p = this.paginator, 54803 c = p.get('nextPageLinkClass'),
54804 label = p.get('nextPageLinkLabel'), 54805 last = p.getTotalPages(), 54806 title = p.get('nextPageLinkTitle'); 54807 54808 this.link = document.createElement('a'); 54809 this.span = document.createElement('span'); 54810 54811 setId(this.link, id_base + '-next-link'); 54812 this.link.href = '#'; 54813 this.link.className = c; 54814 this.link.innerHTML = label; 54815 this.link.title = title; 54816 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); 54817 54818 setId(this.span, id_base + '-next-span'); 54819 this.span.className = c; 54820 this.span.innerHTML = label; 54821 54822 this.current = p.getCurrentPage() === last ? this.span : this.link; 54823 54824 return this.current; 54825 }, 54826 54827 /** 54828 * Swap the link and span nodes if appropriate. 54829 * @method update 54830 * @param e {CustomEvent} The calling change event 54831 */ 54832 update : function (e) { 54833 if (e && e.prevValue === e.newValue) { 54834 return; 54835 } 54836 54837 var last = this.paginator.getTotalPages(), 54838 par = this.current ? this.current.parentNode : null; 54839 54840 if (this.paginator.getCurrentPage() !== last) { 54841 if (par && this.current === this.span) { 54842 par.replaceChild(this.link,this.current); 54843 this.current = this.link; 54844 } 54845 } else if (this.current === this.link) { 54846 if (par) { 54847 par.replaceChild(this.span,this.current); 54848 this.current = this.span; 54849 } 54850 } 54851 }, 54852 54853 /** 54854 * Removes the link/span node and clears event listeners 54855 * @method destroy 54856 * @private 54857 */ 54858 destroy : function () { 54859 YAHOO.util.Event.purgeElement(this.link); 54860 this.current.parentNode.removeChild(this.current); 54861 this.link = this.span = null; 54862 }, 54863 54864 /** 54865 * Listener for the link's onclick event. Passes to setPage method. 54866 * @method onClick 54867 * @param e {DOMEvent} The click event 54868 */ 54869 onClick : function (e) { 54870 YAHOO.util.Event.stopEvent(e); 54871 this.paginator.setPage(this.paginator.getNextPage()); 54872 } 54873}; 54874 54875})(); 54876(function () { 54877 54878var Paginator = YAHOO.widget.Paginator, 54879 l = YAHOO.lang, 54880 setId = YAHOO.util.Dom.generateId; 54881 54882/** 54883 * ui Component to generate the link to jump to the previous page. 54884 * 54885 * @namespace YAHOO.widget.Paginator.ui 54886 * @class PreviousPageLink 54887 * @for YAHOO.widget.Paginator 54888 * 54889 * @constructor 54890 * @param p {Pagintor} Paginator instance to attach to 54891 */ 54892Paginator.ui.PreviousPageLink = function (p) { 54893 this.paginator = p; 54894 54895 p.subscribe('recordOffsetChange',this.update,this,true); 54896 p.subscribe('rowsPerPageChange',this.update,this,true); 54897 p.subscribe('totalRecordsChange',this.update,this,true); 54898 p.subscribe('destroy',this.destroy,this,true); 54899 54900 // TODO: make this work 54901 p.subscribe('previousPageLinkLabelChange',this.update,this,true); 54902 p.subscribe('previousPageLinkClassChange',this.update,this,true); 54903}; 54904 54905/** 54906 * Decorates Paginator instances with new attributes. Called during 54907 * Paginator instantiation. 54908 * @method init 54909 * @param p {Paginator} Paginator instance to decorate 54910 * @static 54911 */ 54912Paginator.ui.PreviousPageLink.init = function (p) { 54913 54914 /** 54915 * Used as innerHTML for the previous page link/span. 54916 * @attribute previousPageLinkLabel 54917 * @default '< prev' 54918 */ 54919 p.setAttributeConfig('previousPageLinkLabel', { 54920 value : '< prev', 54921 validator : l.isString 54922 }); 54923 54924 /** 54925 * CSS class assigned to the link/span 54926 * @attribute previousPageLinkClass 54927 * @default 'yui-pg-previous' 54928 */ 54929 p.setAttributeConfig('previousPageLinkClass', { 54930 value : 'yui-pg-previous', 54931 validator : l.isString 54932 }); 54933 54934 /** 54935 * Used as title for the previous page link. 54936 * @attribute previousPageLinkTitle 54937 * @default 'Previous Page' 54938 */ 54939 p.setAttributeConfig('previousPageLinkTitle', { 54940 value : 'Previous Page', 54941 validator : l.isString 54942 }); 54943 54944}; 54945 54946Paginator.ui.PreviousPageLink.prototype = { 54947 54948 /** 54949 * Currently placed HTMLElement node 54950 * @property current 54951 * @type HTMLElement 54952 * @private 54953 */ 54954 current : null, 54955 54956 /** 54957 * Link node 54958 * @property link 54959 * @type HTMLElement 54960 * @private 54961 */ 54962 link : null, 54963 54964 /** 54965 * Span node (inactive link) 54966 * @property span 54967 * @type HTMLElement 54968 * @private 54969 */ 54970 span : null, 54971 54972 54973 /** 54974 * Generate the nodes and return the appropriate node given the current 54975 * pagination state. 54976 * @method render 54977 * @param id_base {string} used to create unique ids for generated nodes 54978 * @return {HTMLElement} 54979 */ 54980 render : function (id_base) { 54981 var p = this.paginator, 54982 c = p.get('previousPageLinkClass'),
54983 label = p.get('previousPageLinkLabel'), 54984 title = p.get('previousPageLinkTitle'); 54985 54986 this.link = document.createElement('a'); 54987 this.span = document.createElement('span'); 54988 54989 setId(this.link, id_base + '-prev-link'); 54990 this.link.href = '#'; 54991 this.link.className = c; 54992 this.link.innerHTML = label; 54993 this.link.title = title; 54994 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); 54995 54996 setId(this.span, id_base + '-prev-span'); 54997 this.span.className = c; 54998 this.span.innerHTML = label; 54999 55000 this.current = p.getCurrentPage() > 1 ? this.link : this.span; 55001 return this.current; 55002 }, 55003 55004 /** 55005 * Swap the link and span nodes if appropriate. 55006 * @method update 55007 * @param e {CustomEvent} The calling change event 55008 */ 55009 update : function (e) { 55010 if (e && e.prevValue === e.newValue) { 55011 return; 55012 } 55013 55014 var par = this.current ? this.current.parentNode : null; 55015 if (this.paginator.getCurrentPage() > 1) { 55016 if (par && this.current === this.span) { 55017 par.replaceChild(this.link,this.current); 55018 this.current = this.link; 55019 } 55020 } else { 55021 if (par && this.current === this.link) { 55022 par.replaceChild(this.span,this.current); 55023 this.current = this.span; 55024 } 55025 } 55026 }, 55027 55028 /** 55029 * Removes the link/span node and clears event listeners 55030 * @method destroy 55031 * @private 55032 */ 55033 destroy : function () { 55034 YAHOO.util.Event.purgeElement(this.link); 55035 this.current.parentNode.removeChild(this.current); 55036 this.link = this.span = null; 55037 }, 55038 55039 /** 55040 * Listener for the link's onclick event. Passes to setPage method. 55041 * @method onClick 55042 * @param e {DOMEvent} The click event 55043 */ 55044 onClick : function (e) { 55045 YAHOO.util.Event.stopEvent(e); 55046 this.paginator.setPage(this.paginator.getPreviousPage()); 55047 } 55048}; 55049 55050})(); 55051(function () { 55052 55053var Paginator = YAHOO.widget.Paginator, 55054 l = YAHOO.lang, 55055 setId = YAHOO.util.Dom.generateId; 55056 55057/** 55058 * ui Component to generate the rows-per-page dropdown 55059 * 55060 * @namespace YAHOO.widget.Paginator.ui 55061 * @class RowsPerPageDropdown 55062 * @for YAHOO.widget.Paginator 55063 * 55064 * @constructor 55065 * @param p {Pagintor} Paginator instance to attach to 55066 */ 55067Paginator.ui.RowsPerPageDropdown = function (p) { 55068 this.paginator = p; 55069 55070 p.subscribe('rowsPerPageChange',this.update,this,true); 55071 p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true); 55072 p.subscribe('totalRecordsChange',this._handleTotalRecordsChange,this,true); 55073 p.subscribe('destroy',this.destroy,this,true); 55074 55075 // TODO: make this work 55076 p.subscribe('rowsPerPageDropdownClassChange',this.rebuild,this,true); 55077}; 55078 55079/** 55080 * Decorates Paginator instances with new attributes. Called during 55081 * Paginator instantiation. 55082 * @method init 55083 * @param p {Paginator} Paginator instance to decorate 55084 * @static 55085 */ 55086Paginator.ui.RowsPerPageDropdown.init = function (p) { 55087 55088 /** 55089 * Array of available rows-per-page sizes. Converted into select options. 55090 * Array values may be positive integers or object literals in the form<br> 55091 * { value : NUMBER, text : STRING } 55092 * @attribute rowsPerPageOptions 55093 * @default [] 55094 */ 55095 p.setAttributeConfig('rowsPerPageOptions', { 55096 value : [], 55097 validator : l.isArray 55098 }); 55099 55100 /** 55101 * CSS class assigned to the select node 55102 * @attribute rowsPerPageDropdownClass 55103 * @default 'yui-pg-rpp-options' 55104 */ 55105 p.setAttributeConfig('rowsPerPageDropdownClass', { 55106 value : 'yui-pg-rpp-options', 55107 validator : l.isString 55108 }); 55109}; 55110 55111Paginator.ui.RowsPerPageDropdown.prototype = { 55112 55113 /** 55114 * select node 55115 * @property select 55116 * @type HTMLElement 55117 * @private 55118 */ 55119 select : null, 55120 55121 55122 /** 55123 * option node for the optional All value 55124 * 55125 * @property all 55126 * @type HTMLElement 55127 * @protected 55128 */ 55129 all : null, 55130 55131 /** 55132 * Generate the select and option nodes and returns the select node. 55133 * @method render 55134 * @param id_base {string} used to create unique ids for generated nodes 55135 * @return {HTMLElement} 55136 */ 55137 render : function (id_base) { 55138 this.select = document.createElement('select'); 55139 setId(this.select, id_base + '-rpp'); 55140 this.select.className = this.paginator.get('rowsPerPageDropdownClass'); 55141 this.select.title = 'Rows per page'; 55142 55143 YAHOO.util.Event.on(this.select,'change',this.onChange,this,true); 55144 55145 this.rebuild(); 55146 55147 return this.select; 55148 }, 55149 55150 /** 55151 * (Re)generate the select options. 55152 * @method rebuild 55153 */ 55154 rebuild : function (e) { 55155 var p = this.paginator, 55156 sel = this.select, 55157 options = p.get('rowsPerPageOptions'), 55158 opt,cfg,val,i,len; 55159 55160 this.all = null; 55161 55162 for (i = 0, len = options.length; i < len; ++i) { 55163 cfg = options[i]; 55164 opt = sel.options[i] || 55165 sel.appendChild(document.createElement('option')); 55166 val = l.isValue(cfg.value) ? cfg.value : cfg; 55167 opt.text = l.isValue(cfg.text) ? cfg.text : cfg; 55168 55169 if (l.isString(val) && val.toLowerCase() === 'all') { 55170 this.all = opt; 55171 opt.value = p.get('totalRecords'); 55172 } else{ 55173 opt.value = val; 55174 } 55175 55176 } 55177 55178 while (sel.options.length > options.length) { 55179 sel.removeChild(sel.firstChild); 55180 } 55181 55182 this.update(); 55183 }, 55184 55185 /** 55186 * Select the appropriate option if changed. 55187 * @method update 55188 * @param e {CustomEvent} The calling change event 55189 */ 55190 update : function (e) {
55191 if (e && e.prevValue === e.newValue) { 55192 return; 55193 } 55194 55195 var rpp = this.paginator.get('rowsPerPage')+'', 55196 options = this.select.options, 55197 i,len; 55198 55199 for (i = 0, len = options.length; i < len; ++i) { 55200 if (options[i].value === rpp) { 55201 options[i].selected = true; 55202 break; 55203 } 55204 } 55205 }, 55206 55207 /** 55208 * Listener for the select's onchange event. Sent to setRowsPerPage method. 55209 * @method onChange 55210 * @param e {DOMEvent} The change event 55211 */ 55212 onChange : function (e) { 55213 this.paginator.setRowsPerPage( 55214 parseInt(this.select.options[this.select.selectedIndex].value,10)); 55215 }, 55216 55217 /** 55218 * Updates the all option value (and Paginator's rowsPerPage attribute if 55219 * necessary) in response to a change in the Paginator's totalRecords. 55220 * 55221 * @method _handleTotalRecordsChange 55222 * @param e {Event} attribute change event 55223 * @protected 55224 */ 55225 _handleTotalRecordsChange : function (e) { 55226 if (!this.all || (e && e.prevValue === e.newValue)) { 55227 return; 55228 } 55229 55230 this.all.value = e.newValue; 55231 if (this.all.selected) { 55232 this.paginator.set('rowsPerPage',e.newValue); 55233 } 55234 }, 55235 55236 /** 55237 * Removes the select node and clears event listeners 55238 * @method destroy 55239 * @private 55240 */ 55241 destroy : function () { 55242 YAHOO.util.Event.purgeElement(this.select); 55243 this.select.parentNode.removeChild(this.select); 55244 this.select = null; 55245 } 55246}; 55247 55248})(); 55249(function () { 55250 55251var Paginator = YAHOO.widget.Paginator, 55252 l = YAHOO.lang, 55253 setId = YAHOO.util.Dom.generateId; 55254 55255/** 55256 * ui Component to generate the jump-to-page dropdown 55257 * 55258 * @namespace YAHOO.widget.Paginator.ui 55259 * @class JumpToPageDropdown 55260 * @for YAHOO.widget.Paginator 55261 * 55262 * @constructor 55263 * @param p {Pagintor} Paginator instance to attach to 55264 */ 55265Paginator.ui.JumpToPageDropdown = function (p) { 55266 this.paginator = p; 55267 55268 p.subscribe('rowsPerPageChange',this.rebuild,this,true); 55269 p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true); 55270 p.subscribe('pageChange',this.update,this,true); 55271 p.subscribe('totalRecordsChange',this.rebuild,this,true); 55272 p.subscribe('destroy',this.destroy,this,true); 55273 55274}; 55275 55276/** 55277 * Decorates Paginator instances with new attributes. Called during 55278 * Paginator instantiation. 55279 * @method init 55280 * @param p {Paginator} Paginator instance to decorate 55281 * @static 55282 */ 55283Paginator.ui.JumpToPageDropdown.init = function (p) { 55284 55285 55286 55287 /** 55288 * CSS class assigned to the select node 55289 * @attribute jumpToPageDropdownClass 55290 * @default 'yui-pg-jtp-options' 55291 */ 55292 p.setAttributeConfig('jumpToPageDropdownClass', { 55293 value : 'yui-pg-jtp-options', 55294 validator : l.isString 55295 }); 55296}; 55297 55298Paginator.ui.JumpToPageDropdown.prototype = { 55299 55300 /** 55301 * select node 55302 * @property select 55303 * @type HTMLElement 55304 * @private 55305 */ 55306 select : null, 55307 55308 55309 55310 /** 55311 * Generate the select and option nodes and returns the select node. 55312 * @method render 55313 * @param id_base {string} used to create unique ids for generated nodes 55314 * @return {HTMLElement} 55315 */ 55316 render : function (id_base) { 55317 this.select = document.createElement('select'); 55318 setId(this.select, id_base + '-jtp'); 55319 this.select.className = this.paginator.get('jumpToPageDropdownClass'); 55320 this.select.title = 'Jump to page'; 55321 55322 YAHOO.util.Event.on(this.select,'change',this.onChange,this,true); 55323 55324 this.rebuild(); 55325 55326 return this.select; 55327 }, 55328 55329 /** 55330 * (Re)generate the select options. 55331 * @method rebuild 55332 */ 55333 rebuild : function (e) { 55334 var p = this.paginator, 55335 sel = this.select, 55336 numPages = p.getTotalPages(), 55337 opt,i,len; 55338 55339 this.all = null; 55340 55341 for (i = 0, len = numPages; i < len; ++i ) { 55342 opt = sel.options[i] || 55343 sel.appendChild(document.createElement('option')); 55344 55345 opt.innerHTML = i + 1; 55346 55347 opt.value = i + 1; 55348 55349 55350 } 55351 55352 for ( i = numPages, len = sel.options.length ; i < len ; i++ ) { 55353 sel.removeChild(sel.lastChild); 55354 } 55355 55356 this.update(); 55357 }, 55358 55359 /** 55360 * Select the appropriate option if changed. 55361 * @method update 55362 * @param e {CustomEvent} The calling change event 55363 */ 55364 update : function (e) { 55365
55366 if (e && e.prevValue === e.newValue) { 55367 return; 55368 } 55369 55370 var cp = this.paginator.getCurrentPage()+'', 55371 options = this.select.options, 55372 i,len; 55373 55374 for (i = 0, len = options.length; i < len; ++i) { 55375 if (options[i].value === cp) { 55376 options[i].selected = true; 55377 break; 55378 } 55379 } 55380 }, 55381 55382 /** 55383 * Listener for the select's onchange event. Sent to setPage method. 55384 * @method onChange 55385 * @param e {DOMEvent} The change event 55386 */ 55387 onChange : function (e) { 55388 this.paginator.setPage( 55389 parseInt(this.select.options[this.select.selectedIndex].value,false)); 55390 }, 55391 55392 55393 55394 /** 55395 * Removes the select node and clears event listeners 55396 * @method destroy 55397 * @private 55398 */ 55399 destroy : function () { 55400 YAHOO.util.Event.purgeElement(this.select); 55401 this.select.parentNode.removeChild(this.select); 55402 this.select = null; 55403 } 55404}; 55405 55406})(); 55407YAHOO.register("paginator", YAHOO.widget.Paginator, {version: "2.9.0", build: "2800"}); 55408/* 55409Copyright (c) 2011, Yahoo! Inc. All rights reserved. 55410Code licensed under the BSD License: 55411http://developer.yahoo.com/yui/license.html 55412version: 2.9.0 55413*/ 55414/** 55415 * Mechanism to execute a series of callbacks in a non-blocking queue. Each callback is executed via setTimout unless configured with a negative timeout, in which case it is run in blocking mode in the same execution thread as the previous callback. Callbacks can be function references or object literals with the following keys: 55416 * <ul> 55417 * <li><code>method</code> - {Function} REQUIRED the callback function.</li> 55418 * <li><code>scope</code> - {Object} the scope from which to execute the callback. Default is the global window scope.</li> 55419 * <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li> 55420 * <li><code>timeout</code> - {number} millisecond delay to wait after previous callback completion before executing this callback. Negative values cause immediate blocking execution. Default 0.</li> 55421 * <li><code>until</code> - {Function} boolean function executed before each iteration. Return true to indicate completion and proceed to the next callback.</li> 55422 * <li><code>iterations</code> - {Number} number of times to execute the callback before proceeding to the next callback in the chain. Incompatible with <code>until</code>.</li> 55423 * </ul> 55424 * 55425 * @namespace YAHOO.util 55426 * @class Chain 55427 * @constructor 55428 * @param callback* {Function|Object} Any number of callbacks to initialize the queue 55429*/ 55430YAHOO.util.Chain = function () { 55431 /** 55432 * The callback queue 55433 * @property q 55434 * @type {Array} 55435 * @private 55436 */ 55437 this.q = [].slice.call(arguments); 55438 55439 /** 55440 * Event fired when the callback queue is emptied via execution (not via 55441 * a call to chain.stop(). 55442 * @event end 55443 */ 55444 this.createEvent('end'); 55445}; 55446 55447YAHOO.util.Chain.prototype = { 55448 /** 55449 * Timeout id used to pause or stop execution and indicate the execution state of the Chain. 0 indicates paused or stopped, -1 indicates blocking execution, and any positive number indicates non-blocking execution. 55450 * @property id 55451 * @type {number} 55452 * @private 55453 */ 55454 id : 0, 55455 55456 /** 55457 * Begin executing the chain, or resume execution from the last paused position. 55458 * @method run 55459 * @return {Chain} the Chain instance 55460 */ 55461 run : function () { 55462 // Grab the first callback in the queue 55463 var c = this.q[0], 55464 fn; 55465 55466 // If there is no callback in the queue or the Chain is currently 55467 // in an execution mode, return 55468 if (!c) { 55469 this.fireEvent('end'); 55470 return this; 55471 } else if (this.id) { 55472 return this; 55473 } 55474 55475 fn = c.method || c; 55476 55477 if (typeof fn === 'function') { 55478 var o = c.scope || {}, 55479 args = c.argument || [], 55480 ms = c.timeout || 0, 55481 me = this; 55482 55483 if (!(args instanceof Array)) { 55484 args = [args]; 55485 } 55486
55487 // Execute immediately if the callback timeout is negative. 55488 if (ms < 0) { 55489 this.id = ms; 55490 if (c.until) { 55491 for (;!c.until();) { 55492 // Execute the callback from scope, with argument 55493 fn.apply(o,args); 55494 } 55495 } else if (c.iterations) { 55496 for (;c.iterations-- > 0;) { 55497 fn.apply(o,args); 55498 } 55499 } else { 55500 fn.apply(o,args); 55501 } 55502 this.q.shift(); 55503 this.id = 0; 55504 return this.run(); 55505 } else { 55506 // If the until condition is set, check if we're done 55507 if (c.until) { 55508 if (c.until()) { 55509 // Shift this callback from the queue and execute the next 55510 // callback 55511 this.q.shift(); 55512 return this.run(); 55513 } 55514 // Otherwise if either iterations is not set or we're 55515 // executing the last iteration, shift callback from the queue 55516 } else if (!c.iterations || !--c.iterations) { 55517 this.q.shift(); 55518 } 55519 55520 // Otherwise set to execute after the configured timeout 55521 this.id = setTimeout(function () { 55522 // Execute the callback from scope, with argument 55523 fn.apply(o,args); 55524 // Check if the Chain was not paused from inside the callback 55525 if (me.id) { 55526 // Indicate ready to run state 55527 me.id = 0; 55528 // Start the fun all over again 55529 me.run(); 55530 } 55531 },ms); 55532 } 55533 } 55534 55535 return this; 55536 }, 55537 55538 /** 55539 * Add a callback to the end of the queue 55540 * @method add 55541 * @param c {Function|Object} the callback function ref or object literal 55542 * @return {Chain} the Chain instance 55543 */ 55544 add : function (c) { 55545 this.q.push(c); 55546 return this; 55547 }, 55548 55549 /** 55550 * Pause the execution of the Chain after the current execution of the 55551 * current callback completes. If called interstitially, clears the 55552 * timeout for the pending callback. Paused Chains can be restarted with 55553 * chain.run() 55554 * @method pause 55555 * @return {Chain} the Chain instance 55556 */ 55557 pause: function () { 55558 // Conditional added for Caja compatibility 55559 if (this.id > 0) { 55560 clearTimeout(this.id); 55561 } 55562 this.id = 0; 55563 return this; 55564 }, 55565 55566 /** 55567 * Stop and clear the Chain's queue after the current execution of the 55568 * current callback completes. 55569 * @method stop 55570 * @return {Chain} the Chain instance 55571 */ 55572 stop : function () { 55573 this.pause(); 55574 this.q = []; 55575 return this; 55576 } 55577}; 55578YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider); 55579 55580/** 55581 * Augments the Event Utility with a <code>delegate</code> method that 55582 * facilitates easy creation of delegated event listeners. (Note: Using CSS 55583 * selectors as the filtering criteria for delegated event listeners requires 55584 * inclusion of the Selector Utility.) 55585 * 55586 * @module event-delegate 55587 * @title Event Utility Event Delegation Module 55588 * @namespace YAHOO.util 55589 * @requires event 55590 */ 55591 55592(function () { 55593 55594 var Event = YAHOO.util.Event, 55595 Lang = YAHOO.lang, 55596 delegates = [], 55597 55598 55599 getMatch = function(el, selector, container) { 55600 55601 var returnVal; 55602 55603 if (!el || el === container) { 55604 returnVal = false;
55605 } 55606 else { 55607 returnVal = YAHOO.util.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container); 55608 } 55609 55610 return returnVal; 55611 55612 }; 55613 55614 55615 Lang.augmentObject(Event, { 55616 55617 /** 55618 * Creates a delegate function used to call event listeners specified 55619 * via the <code>YAHOO.util.Event.delegate</code> method. 55620 * 55621 * @method _createDelegate 55622 * 55623 * @param {Function} fn The method (event listener) to call. 55624 * @param {Function|string} filter Function or CSS selector used to 55625 * determine for what element(s) the event listener should be called. 55626 * @param {Object} obj An arbitrary object that will be 55627 * passed as a parameter to the listener. 55628 * @param {Boolean|object} overrideContext If true, the value of the 55629 * obj parameter becomes the execution context 55630 * of the listener. If an object, this object 55631 * becomes the execution context. 55632 * @return {Function} Function that will call the event listener 55633 * specified by the <code>YAHOO.util.Event.delegate</code> method. 55634 * @private 55635 * @for Event 55636 * @static 55637 */ 55638 _createDelegate: function (fn, filter, obj, overrideContext) { 55639 55640 return function (event) { 55641 55642 var container = this, 55643 target = Event.getTarget(event), 55644 selector = filter, 55645 55646 // The user might have specified the document object 55647 // as the delegation container, in which case it is not 55648 // nessary to scope the provided CSS selector(s) to the 55649 // delegation container 55650 bDocument = (container.nodeType === 9), 55651 55652 matchedEl, 55653 context, 55654 sID, 55655 sIDSelector; 55656 55657 55658 if (Lang.isFunction(filter)) { 55659 matchedEl = filter(target); 55660 } 55661 else if (Lang.isString(filter)) { 55662 55663 if (!bDocument) { 55664 55665 sID = container.id; 55666 55667 if (!sID) { 55668 sID = Event.generateId(container); 55669 } 55670 55671 // Scope all selectors to the container 55672 sIDSelector = ("#" + sID + " "); 55673 selector = (sIDSelector + filter).replace(/,/gi, ("," + sIDSelector)); 55674 55675 } 55676 55677 55678 if (YAHOO.util.Selector.test(target, selector)) { 55679 matchedEl = target; 55680 } 55681 else if (YAHOO.util.Selector.test(target, ((selector.replace(/,/gi, " *,")) + " *"))) { 55682 55683 // The target is a descendant of an element matching 55684 // the selector, so crawl up to find the ancestor that 55685 // matches the selector 55686 55687 matchedEl = getMatch(target, selector, container); 55688 55689 } 55690 55691 } 55692 55693 55694 if (matchedEl) { 55695 55696 // The default context for delegated listeners is the 55697 // element that matched the filter. 55698 55699 context = matchedEl; 55700 55701 if (overrideContext) { 55702 if (overrideContext === true) { 55703 context = obj; 55704 } else { 55705 context = overrideContext; 55706 } 55707 } 55708 55709 // Call the listener passing in the container and the 55710 // element that matched the filter in case the user 55711 // needs those. 55712 55713 return fn.call(context, event, matchedEl, container, obj); 55714 55715 } 55716 55717 }; 55718 55719 }, 55720 55721 55722 /** 55723 * Appends a delegated event listener. Delegated event listeners 55724 * receive three arguments by default: the DOM event, the element 55725 * specified by the filtering function or CSS selector, and the 55726 * container element (the element to which the event listener is 55727 * bound). (Note: Using the delegate method requires the event-delegate 55728 * module. Using CSS selectors as the filtering criteria for delegated 55729 * event listeners requires inclusion of the Selector Utility.) 55730 * 55731 * @method delegate 55732 * 55733 * @param {String|HTMLElement|Array|NodeList} container An id, an element 55734 * reference, or a collection of ids and/or elements to assign the 55735 * listener to. 55736 * @param {String} type The type of event listener to append 55737 * @param {Function} fn The method the event invokes 55738 * @param {Function|string} filter Function or CSS selector used to
55739 * determine for what element(s) the event listener should be called. 55740 * When a function is specified, the function should return an 55741 * HTML element. Using a CSS Selector requires the inclusion of the 55742 * CSS Selector Utility. 55743 * @param {Object} obj An arbitrary object that will be 55744 * passed as a parameter to the listener 55745 * @param {Boolean|object} overrideContext If true, the value of the obj parameter becomes 55746 * the execution context of the listener. If an 55747 * object, this object becomes the execution 55748 * context. 55749 * @return {Boolean} Returns true if the action was successful or defered, 55750 * false if one or more of the elements 55751 * could not have the listener attached, 55752 * or if the operation throws an exception. 55753 * @static 55754 * @for Event 55755 */ 55756 delegate: function (container, type, fn, filter, obj, overrideContext) { 55757 55758 var sType = type, 55759 fnMouseDelegate, 55760 fnDelegate; 55761 55762 55763 if (Lang.isString(filter) && !YAHOO.util.Selector) { 55764 YAHOO.log("Using a CSS selector to define the filtering criteria for a delegated listener requires the Selector Utility.", "error", "Event"); 55765 return false; 55766 } 55767 55768 55769 if (type == "mouseenter" || type == "mouseleave") { 55770 55771 if (!Event._createMouseDelegate) { 55772 YAHOO.log("Delegating a " + type + " event requires the event-mouseenter module.", "error", "Event"); 55773 return false; 55774 } 55775 55776 // Look up the real event--either mouseover or mouseout 55777 sType = Event._getType(type); 55778 55779 fnMouseDelegate = Event._createMouseDelegate(fn, obj, overrideContext); 55780 55781 fnDelegate = Event._createDelegate(function (event, matchedEl, container) { 55782 55783 return fnMouseDelegate.call(matchedEl, event, container); 55784 55785 }, filter, obj, overrideContext); 55786 55787 } 55788 else { 55789 55790 fnDelegate = Event._createDelegate(fn, filter, obj, overrideContext); 55791 55792 } 55793 55794 delegates.push([container, sType, fn, fnDelegate]); 55795 55796 return Event.on(container, sType, fnDelegate); 55797 55798 }, 55799 55800 55801 /** 55802 * Removes a delegated event listener. 55803 * 55804 * @method removeDelegate 55805 * 55806 * @param {String|HTMLElement|Array|NodeList} container An id, an element 55807 * reference, or a collection of ids and/or elements to remove 55808 * the listener from. 55809 * @param {String} type The type of event to remove. 55810 * @param {Function} fn The method the event invokes. If fn is 55811 * undefined, then all event listeners for the type of event are 55812 * removed. 55813 * @return {boolean} Returns true if the unbind was successful, false 55814 * otherwise. 55815 * @static 55816 * @for Event 55817 */ 55818 removeDelegate: function (container, type, fn) { 55819 55820 var sType = type, 55821 returnVal = false, 55822 index, 55823 cacheItem; 55824 55825 // Look up the real event--either mouseover or mouseout 55826 if (type == "mouseenter" || type == "mouseleave") { 55827 sType = Event._getType(type); 55828 } 55829 55830 index = Event._getCacheIndex(delegates, container, sType, fn); 55831 55832 if (index >= 0) { 55833 cacheItem = delegates[index]; 55834 } 55835 55836 55837 if (container && cacheItem) { 55838 55839 returnVal = Event.removeListener(cacheItem[0], cacheItem[1], cacheItem[3]); 55840 55841 if (returnVal) { 55842 delete delegates[index][2]; 55843 delete delegates[index][3]; 55844 delegates.splice(index, 1); 55845 } 55846 55847 } 55848 55849 return returnVal; 55850 55851 } 55852 55853 }); 55854 55855}()); 55856 55857 55858/** 55859 * Augments the Event Utility with support for the mouseenter and mouseleave 55860 * events: A mouseenter event fires the first time the mouse enters an 55861 * element; a mouseleave event first the first time the mouse leaves an 55862 * element. 55863 * 55864 * @module event-mouseenter 55865 * @title Event Utility mouseenter and mouseout Module 55866 * @namespace YAHOO.util 55867 * @requires event 55868 */ 55869 55870(function () { 55871 55872 var Event = YAHOO.util.Event, 55873 Lang = YAHOO.lang, 55874 55875 addListener = Event.addListener, 55876 removeListener = Event.removeListener, 55877 getListeners = Event.getListeners, 55878 55879 delegates = [], 55880 55881 specialTypes = { 55882 mouseenter: "mouseover", 55883 mouseleave: "mouseout" 55884 }, 55885 55886 remove = function(el, type, fn) { 55887 55888 var index = Event._getCacheIndex(delegates, el, type, fn), 55889 cacheItem, 55890 returnVal; 55891 55892 if (index >= 0) { 55893 cacheItem = delegates[index]; 55894 } 55895 55896 if (el && cacheItem) { 55897 55898 // removeListener will translate the value of type 55899 returnVal = removeListener.call(Event, cacheItem[0], type, cacheItem[3]); 55900 55901 if (returnVal) { 55902 delete delegates[index][2]; 55903 delete delegates[index][3]; 55904 delegates.splice(index, 1); 55905 } 55906 55907 } 55908 55909 return returnVal; 55910 55911 }; 55912 55913 55914 Lang.augmentObject(Event._specialTypes, specialTypes); 55915 55916 Lang.augmentObject(Event, { 55917 55918 /** 55919 * Creates a delegate function used to call mouseover and mouseleave 55920 * event listeners specified via the 55921 * <code>YAHOO.util.Event.addListener</code> 55922 * or <code>YAHOO.util.Event.on</code> method. 55923 *
55924 * @method _createMouseDelegate 55925 * 55926 * @param {Function} fn The method (event listener) to call 55927 * @param {Object} obj An arbitrary object that will be 55928 * passed as a parameter to the listener 55929 * @param {Boolean|object} overrideContext If true, the value of the 55930 * obj parameter becomes the execution context 55931 * of the listener. If an object, this object 55932 * becomes the execution context. 55933 * @return {Function} Function that will call the event listener 55934 * specified by either the <code>YAHOO.util.Event.addListener</code> 55935 * or <code>YAHOO.util.Event.on</code> method. 55936 * @private 55937 * @static 55938 * @for Event 55939 */ 55940 _createMouseDelegate: function (fn, obj, overrideContext) { 55941 55942 return function (event, container) { 55943 55944 var el = this, 55945 relatedTarget = Event.getRelatedTarget(event), 55946 context, 55947 args; 55948 55949 if (el != relatedTarget && !YAHOO.util.Dom.isAncestor(el, relatedTarget)) { 55950 55951 context = el; 55952 55953 if (overrideContext) { 55954 if (overrideContext === true) { 55955 context = obj; 55956 } else { 55957 context = overrideContext; 55958 } 55959 } 55960 55961 // The default args passed back to a mouseenter or 55962 // mouseleave listener are: the event, and any object 55963 // the user passed when subscribing 55964 55965 args = [event, obj]; 55966 55967 // Add the element and delegation container as arguments 55968 // when delegating mouseenter and mouseleave 55969 55970 if (container) { 55971 args.splice(1, 0, el, container); 55972 } 55973 55974 return fn.apply(context, args); 55975 55976 } 55977 55978 }; 55979 55980 }, 55981 55982 addListener: function (el, type, fn, obj, overrideContext) { 55983 55984 var fnDelegate, 55985 returnVal; 55986 55987 if (specialTypes[type]) { 55988 55989 fnDelegate = Event._createMouseDelegate(fn, obj, overrideContext); 55990 55991 fnDelegate.mouseDelegate = true; 55992 55993 delegates.push([el, type, fn, fnDelegate]); 55994 55995 // addListener will translate the value of type 55996 returnVal = addListener.call(Event, el, type, fnDelegate); 55997 55998 } 55999 else { 56000 returnVal = addListener.apply(Event, arguments); 56001 } 56002 56003 return returnVal; 56004 56005 }, 56006 56007 removeListener: function (el, type, fn) { 56008 56009 var returnVal; 56010 56011 if (specialTypes[type]) { 56012 returnVal = remove.apply(Event, arguments); 56013 } 56014 else { 56015 returnVal = removeListener.apply(Event, arguments); 56016 } 56017 56018 return returnVal; 56019 56020 }, 56021 56022 getListeners: function (el, type) { 56023 56024 // If the user specified the type as mouseover or mouseout, 56025 // need to filter out those used by mouseenter and mouseleave. 56026 // If the user specified the type as mouseenter or mouseleave, 56027 // need to filter out the true mouseover and mouseout listeners. 56028 56029 var listeners = [], 56030 elListeners, 56031 bMouseOverOrOut = (type === "mouseover" || type === "mouseout"), 56032 bMouseDelegate, 56033 i, 56034 l; 56035 56036 if (type && (bMouseOverOrOut || specialTypes[type])) { 56037 56038 elListeners = getListeners.call(Event, el, this._getType(type)); 56039 56040 if (elListeners) { 56041 56042 for (i=elListeners.length-1; i>-1; i--) { 56043 56044 l = elListeners[i]; 56045 bMouseDelegate = l.fn.mouseDelegate; 56046 56047 if ((specialTypes[type] && bMouseDelegate) || (bMouseOverOrOut && !bMouseDelegate)) { 56048 listeners.push(l); 56049 } 56050 56051 } 56052 56053 } 56054 56055 } 56056 else { 56057 listeners = getListeners.apply(Event, arguments); 56058 } 56059 56060 return (listeners && listeners.length) ? listeners : null; 56061 56062 } 56063 56064 }, true); 56065 56066 Event.on = Event.addListener; 56067 56068}()); 56069YAHOO.register("event-mouseenter", YAHOO.util.Event, {version: "2.9.0", build: "2800"}); 56070 56071var Y = YAHOO, 56072 Y_DOM = YAHOO.util.Dom, 56073 EMPTY_ARRAY = [], 56074 Y_UA = Y.env.ua, 56075 Y_Lang = Y.lang, 56076 Y_DOC = document, 56077 Y_DOCUMENT_ELEMENT = Y_DOC.documentElement, 56078
56079 Y_DOM_inDoc = Y_DOM.inDocument, 56080 Y_mix = Y_Lang.augmentObject, 56081 Y_guid = Y_DOM.generateId, 56082 56083 Y_getDoc = function(element) { 56084 var doc = Y_DOC; 56085 if (element) { 56086 doc = (element.nodeType === 9) ? element : // element === document 56087 element.ownerDocument || // element === DOM node 56088 element.document || // element === window 56089 Y_DOC; // default 56090 } 56091 56092 return doc; 56093 }, 56094 56095 Y_Array = function(o, startIdx) { 56096 var l, a, start = startIdx || 0; 56097 56098 // IE errors when trying to slice HTMLElement collections 56099 try { 56100 return Array.prototype.slice.call(o, start); 56101 } catch (e) { 56102 a = []; 56103 l = o.length; 56104 for (; start < l; start++) { 56105 a.push(o[start]); 56106 } 56107 return a; 56108 } 56109 }, 56110 56111 Y_DOM_allById = function(id, root) { 56112 root = root || Y_DOC; 56113 var nodes = [], 56114 ret = [], 56115 i, 56116 node; 56117 56118 if (root.querySelectorAll) { 56119 ret = root.querySelectorAll('[id="' + id + '"]'); 56120 } else if (root.all) { 56121 nodes = root.all(id); 56122 56123 if (nodes) { 56124 // root.all may return HTMLElement or HTMLCollection. 56125 // some elements are also HTMLCollection (FORM, SELECT). 56126 if (nodes.nodeName) { 56127 if (nodes.id === id) { // avoid false positive on name 56128 ret.push(nodes); 56129 nodes = EMPTY_ARRAY; // done, no need to filter 56130 } else { // prep for filtering 56131 nodes = [nodes]; 56132 } 56133 } 56134 56135 if (nodes.length) { 56136 // filter out matches on node.name 56137 // and element.id as reference to element with id === 'id' 56138 for (i = 0; node = nodes[i++];) { 56139 if (node.id === id || 56140 (node.attributes && node.attributes.id && 56141 node.attributes.id.value === id)) { 56142 ret.push(node); 56143 } 56144 } 56145 } 56146 } 56147 } else { 56148 ret = [Y_getDoc(root).getElementById(id)]; 56149 } 56150 56151 return ret; 56152 }; 56153 56154/** 56155 * The selector-native module provides support for native querySelector 56156 * @module dom 56157 * @submodule selector-native 56158 * @for Selector 56159 */ 56160 56161/** 56162 * Provides support for using CSS selectors to query the DOM 56163 * @class Selector 56164 * @static 56165 * @for Selector 56166 */ 56167 56168var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', 56169 OWNER_DOCUMENT = 'ownerDocument', 56170 56171Selector = { 56172 _foundCache: [], 56173 56174 useNative: true, 56175 56176 _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ? 56177 function(nodeA, nodeB) { 56178 var a = nodeA.sourceIndex, 56179 b = nodeB.sourceIndex; 56180 56181 if (a === b) { 56182 return 0; 56183 } else if (a > b) { 56184 return 1; 56185 } 56186 56187 return -1; 56188 56189 } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ? 56190 function(nodeA, nodeB) { 56191 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { 56192 return -1; 56193 } else { 56194 return 1; 56195 } 56196 } : 56197 function(nodeA, nodeB) { 56198 var rangeA, rangeB, compare; 56199 if (nodeA && nodeB) { 56200 rangeA = nodeA[OWNER_DOCUMENT].createRange(); 56201 rangeA.setStart(nodeA, 0); 56202 rangeB = nodeB[OWNER_DOCUMENT].createRange(); 56203 rangeB.setStart(nodeB, 0); 56204 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END 56205 } 56206 56207 return compare; 56208 56209 }), 56210 56211 _sort: function(nodes) { 56212 if (nodes) { 56213 nodes = Y_Array(nodes, 0, true); 56214 if (nodes.sort) { 56215 nodes.sort(Selector._compare); 56216 } 56217 } 56218 56219 return nodes; 56220 }, 56221 56222 _deDupe: function(nodes) { 56223 var ret = [], 56224 i, node; 56225 56226 for (i = 0; (node = nodes[i++]);) { 56227 if (!node._found) { 56228 ret[ret.length] = node; 56229 node._found = true; 56230 } 56231 } 56232 56233 for (i = 0; (node = ret[i++]);) { 56234 node._found = null; 56235 node.removeAttribute('_found'); 56236 } 56237 56238 return ret; 56239 }, 56240 56241 /** 56242 * Retrieves a set of nodes based on a given CSS selector. 56243 * @method query 56244 * 56245 * @param {string} selector The CSS Selector to test the node against. 56246 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc 56247 * @param {Boolean} firstOnly optional Whether or not to return only the first match. 56248 * @return {Array} An array of nodes that match the given selector. 56249 * @static 56250 */ 56251 query: function(selector, root, firstOnly, skipNative) { 56252 if (typeof root == 'string') { 56253 root = Y_DOM.get(root); 56254 if (!root) { 56255 return (firstOnly) ? null : []; 56256 } 56257 } else { 56258 root = root || Y_DOC; 56259 } 56260 56261 var ret = [], 56262 useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative), 56263 queries = [[selector, root]], 56264 query, 56265 result, 56266 i, 56267 fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery; 56268 56269 if (selector && fn) { 56270 // split group into seperate queries 56271 if (!skipNative && // already done if skipping 56272 (!useNative || root.tagName)) { // split native when element scoping is needed 56273 queries = Selector._splitQueries(selector, root); 56274 } 56275 56276 for (i = 0; (query = queries[i++]);) {
56277 result = fn(query[0], query[1], firstOnly); 56278 if (!firstOnly) { // coerce DOM Collection to Array 56279 result = Y_Array(result, 0, true); 56280 } 56281 if (result) { 56282 ret = ret.concat(result); 56283 } 56284 } 56285 56286 if (queries.length > 1) { // remove dupes and sort by doc order 56287 ret = Selector._sort(Selector._deDupe(ret)); 56288 } 56289 } 56290 56291 Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector'); 56292 return (firstOnly) ? (ret[0] || null) : ret; 56293 56294 }, 56295 56296 // allows element scoped queries to begin with combinator 56297 // e.g. query('> p', document.body) === query('body > p') 56298 _splitQueries: function(selector, node) { 56299 var groups = selector.split(','), 56300 queries = [], 56301 prefix = '', 56302 i, len; 56303 56304 if (node) { 56305 // enforce for element scoping 56306 if (node.tagName) { 56307 node.id = node.id || Y_guid(); 56308 prefix = '[id="' + node.id + '"] '; 56309 } 56310 56311 for (i = 0, len = groups.length; i < len; ++i) { 56312 selector = prefix + groups[i]; 56313 queries.push([selector, node]); 56314 } 56315 } 56316 56317 return queries; 56318 }, 56319 56320 _nativeQuery: function(selector, root, one) { 56321 if (Y_UA.webkit && selector.indexOf(':checked') > -1 && 56322 (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected" 56323 return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query 56324 } 56325 try { 56326 //Y.log('trying native query with: ' + selector, 'info', 'selector-native'); 56327 return root['querySelector' + (one ? '' : 'All')](selector); 56328 } catch(e) { // fallback to brute if available 56329 //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native'); 56330 return Selector.query(selector, root, one, true); // redo with skipNative true 56331 } 56332 }, 56333 56334 filter: function(nodes, selector) { 56335 var ret = [], 56336 i, node; 56337 56338 if (nodes && selector) { 56339 for (i = 0; (node = nodes[i++]);) { 56340 if (Selector.test(node, selector)) { 56341 ret[ret.length] = node; 56342 } 56343 } 56344 } else { 56345 Y.log('invalid filter input (nodes: ' + nodes + 56346 ', selector: ' + selector + ')', 'warn', 'Selector'); 56347 } 56348 56349 return ret; 56350 }, 56351 56352 test: function(node, selector, root) { 56353 var ret = false, 56354 groups = selector.split(','), 56355 useFrag = false, 56356 parent, 56357 item, 56358 items, 56359 frag, 56360 i, j, group; 56361 56362 if (node && node.tagName) { // only test HTMLElements 56363 56364 // we need a root if off-doc 56365 if (!root && !Y_DOM_inDoc(node)) { 56366 parent = node.parentNode; 56367 if (parent) { 56368 root = parent; 56369 } else { // only use frag when no parent to query 56370 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 56371 frag.appendChild(node); 56372 root = frag; 56373 useFrag = true; 56374 } 56375 } 56376 root = root || node[OWNER_DOCUMENT]; 56377 56378 if (!node.id) { 56379 node.id = Y_guid(); 56380 } 56381 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 56382 group += '[id="' + node.id + '"]'; 56383 items = Selector.query(group, root); 56384 56385 for (j = 0; item = items[j++];) { 56386 if (item === node) { 56387 ret = true; 56388 break; 56389 } 56390 } 56391 if (ret) { 56392 break; 56393 } 56394 } 56395 56396 if (useFrag) { // cleanup 56397 frag.removeChild(node); 56398 } 56399 } 56400 56401 return ret; 56402 } 56403 56404}; 56405 56406YAHOO.util.Selector = Selector; 56407/** 56408 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. 56409 * @module dom 56410 * @submodule selector-css2 56411 * @for Selector 56412 */ 56413 56414/** 56415 * Provides helper methods for collecting and filtering DOM elements. 56416 */ 56417 56418var PARENT_NODE = 'parentNode', 56419 TAG_NAME = 'tagName', 56420 ATTRIBUTES = 'attributes', 56421 COMBINATOR = 'combinator', 56422 PSEUDOS = 'pseudos', 56423 56424 SelectorCSS2 = { 56425 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move? 56426 SORT_RESULTS: true, 56427 _children: function(node, tag) { 56428 var ret = node.children, 56429 i, 56430 children = [], 56431 childNodes, 56432 child; 56433 56434 if (node.children && tag && node.children.tags) { 56435 children = node.children.tags(tag); 56436 } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children 56437 childNodes = ret || node.childNodes; 56438 ret = []; 56439 for (i = 0; (child = childNodes[i++]);) { 56440 if (child.tagName) { 56441 if (!tag || tag === child.tagName) {
56442 ret.push(child); 56443 } 56444 } 56445 } 56446 } 56447 56448 return ret || []; 56449 }, 56450 56451 _re: { 56452 //attr: /(\[.*\])/g, 56453 attr: /(\[[^\]]*\])/g, 56454 //esc: /\\[:\[][\w\d\]]*/gi, 56455 esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi, 56456 //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i 56457 pseudos: /(\([^\)]*\))/g 56458 }, 56459 56460 /** 56461 * Mapping of shorthand tokens to corresponding attribute selector 56462 * @property shorthand 56463 * @type object 56464 */ 56465 shorthand: { 56466 //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]', 56467 '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]', 56468 //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]', 56469 //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]' 56470 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' 56471 }, 56472 56473 /** 56474 * List of operators and corresponding boolean functions. 56475 * These functions are passed the attribute and the current node's value of the attribute. 56476 * @property operators 56477 * @type object 56478 */ 56479 operators: { 56480 '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute 56481 //'': '.+', 56482 //'=': '^{val}$', // equality 56483 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited 56484 '|=': '^{val}(?:-|$)' // optional hyphen-delimited 56485 }, 56486 56487 pseudos: { 56488 'first-child': function(node) { 56489 return Selector._children(node[PARENT_NODE])[0] === node; 56490 } 56491 }, 56492 56493 _bruteQuery: function(selector, root, firstOnly) { 56494 var ret = [], 56495 nodes = [], 56496 tokens = Selector._tokenize(selector), 56497 token = tokens[tokens.length - 1], 56498 rootDoc = Y_getDoc(root), 56499 child, 56500 id, 56501 className, 56502 tagName; 56503 56504 56505 // if we have an initial ID, set to root when in document 56506 /* 56507 if (tokens[0] && rootDoc === root && 56508 (id = tokens[0].id) && 56509 rootDoc.getElementById(id)) { 56510 root = rootDoc.getElementById(id); 56511 } 56512 */ 56513 56514 if (token) { 56515 // prefilter nodes 56516 id = token.id; 56517 className = token.className; 56518 tagName = token.tagName || '*'; 56519 56520 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags 56521 // try ID first, unless no root.all && root not in document 56522 // (root.all works off document, but not getElementById) 56523 // TODO: move to allById? 56524 if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) { 56525 nodes = Y_DOM_allById(id, root); 56526 // try className 56527 } else if (className) { 56528 nodes = root.getElementsByClassName(className); 56529 } else { // default to tagName 56530 nodes = root.getElementsByTagName(tagName); 56531 } 56532 56533 } else { // brute getElementsByTagName('*') 56534 child = root.firstChild; 56535 while (child) { 56536 if (child.tagName) { // only collect HTMLElements 56537 nodes.push(child); 56538 } 56539 child = child.nextSilbing || child.firstChild; 56540 } 56541 } 56542 if (nodes.length) { 56543 ret = Selector._filterNodes(nodes, tokens, firstOnly); 56544 } 56545 } 56546 56547 return ret; 56548 }, 56549 56550 _filterNodes: function(nodes, tokens, firstOnly) { 56551 var i = 0, 56552 j, 56553 len = tokens.length, 56554 n = len - 1, 56555 result = [], 56556 node = nodes[0], 56557 tmpNode = node, 56558 getters = Selector.getters, 56559 operator, 56560 combinator, 56561 token, 56562 path, 56563 pass, 56564 //FUNCTION = 'function', 56565 value, 56566 tests, 56567 test; 56568 56569 //do { 56570 for (i = 0; (tmpNode = node = nodes[i++]);) { 56571 n = len - 1; 56572 path = null; 56573 56574 testLoop: 56575 while (tmpNode && tmpNode.tagName) { 56576 token = tokens[n]; 56577 tests = token.tests; 56578 j = tests.length; 56579 if (j && !pass) { 56580 while ((test = tests[--j])) { 56581 operator = test[1]; 56582 if (getters[test[0]]) { 56583 value = getters[test[0]](tmpNode, test[0]); 56584 } else { 56585 value = tmpNode[test[0]]; 56586 // use getAttribute for non-standard attributes 56587 if (value === undefined && tmpNode.getAttribute) { 56588 value = tmpNode.getAttribute(test[0]); 56589 } 56590 } 56591 56592 if ((operator === '=' && value !== test[2]) || // fast path for equality 56593 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) 56594 operator.test && !operator.test(value)) || // regex test 56595 (!operator.test && // protect against RegExp as function (webkit) 56596 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test 56597 56598 // skip non element nodes or non-matching tags 56599 if ((tmpNode = tmpNode[path])) { 56600 while (tmpNode && 56601 (!tmpNode.tagName || 56602 (token.tagName && token.tagName !== tmpNode.tagName)) 56603 ) { 56604 tmpNode = tmpNode[path]; 56605 } 56606 } 56607 continue testLoop; 56608 } 56609 } 56610 } 56611 56612 n--; // move to next token 56613 // now that we've passed the test, move up the tree by combinator 56614 if (!pass && (combinator = token.combinator)) { 56615 path = combinator.axis; 56616 tmpNode = tmpNode[path]; 56617 56618 // skip non element nodes 56619 while (tmpNode && !tmpNode.tagName) { 56620 tmpNode = tmpNode[path]; 56621 } 56622 56623 if (combinator.direct) { // one pass only 56624 path = null; 56625 } 56626 56627 } else { // success if we made it this far 56628 result.push(node); 56629 if (firstOnly) { 56630 return result; 56631 } 56632 break; 56633 } 56634 } 56635 }// while (tmpNode = node = nodes[++i]); 56636 node = tmpNode = null; 56637 return result; 56638 }, 56639 56640 combinators: { 56641 ' ': { 56642 axis: 'parentNode' 56643 }, 56644 56645 '>': { 56646 axis: 'parentNode', 56647 direct: true 56648 }, 56649 56650 56651 '+': { 56652 axis: 'previousSibling', 56653 direct: true 56654 } 56655 }, 56656 56657 _parsers: [ 56658 { 56659 name: ATTRIBUTES, 56660 //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, 56661 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, 56662 fn: function(match, token) { 56663 var operator = match[2] || '', 56664 operators = Selector.operators, 56665 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', 56666 test; 56667 56668 // add prefiltering for ID and CLASS 56669 if ((match[1] === 'id' && operator === '=') || 56670 (match[1] === 'className' && 56671 Y_DOCUMENT_ELEMENT.getElementsByClassName && 56672 (operator === '~=' || operator === '='))) { 56673 token.prefilter = match[1]; 56674 56675 56676 match[3] = escVal; 56677 56678 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) 56679 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; 56680 56681 } 56682 56683 // add tests 56684 if (operator in operators) { 56685 test = operators[operator]; 56686 if (typeof test === 'string') { 56687 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); 56688 test = new RegExp(test.replace('{val}', match[3])); 56689 } 56690 match[2] = test; 56691 } 56692 if (!token.last || token.prefilter !== match[1]) { 56693 return match.slice(1); 56694 } 56695 } 56696 56697 }, 56698 { 56699 name: TAG_NAME, 56700 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, 56701 fn: function(match, token) { 56702 var tag = match[1].toUpperCase(); 56703 token.tagName = tag; 56704 56705 if (tag !== '*' && (!token.last || token.prefilter)) { 56706 return [TAG_NAME, '=', tag]; 56707 } 56708 if (!token.prefilter) { 56709 token.prefilter = 'tagName'; 56710 } 56711 } 56712 }, 56713 { 56714 name: COMBINATOR, 56715 re: /^\s*([>+~]|\s)\s*/, 56716 fn: function(match, token) { 56717 } 56718 }, 56719 { 56720 name: PSEUDOS, 56721 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, 56722 fn: function(match, token) { 56723 var test = Selector[PSEUDOS][match[1]]; 56724 if (test) { // reorder match array and unescape special chars for tests 56725 if (match[2]) { 56726 match[2] = match[2].replace(/\\/g, ''); 56727 } 56728 return [match[2], test]; 56729 } else { // selector token not supported (possibly missing CSS3 module) 56730 return false;
56731 } 56732 } 56733 } 56734 ], 56735 56736 _getToken: function(token) { 56737 return { 56738 tagName: null, 56739 id: null, 56740 className: null, 56741 attributes: {}, 56742 combinator: null, 56743 tests: [] 56744 }; 56745 }, 56746 56747 /** 56748 Break selector into token units per simple selector. 56749 Combinator is attached to the previous token. 56750 */ 56751 _tokenize: function(selector) { 56752 selector = selector || ''; 56753 selector = Selector._replaceShorthand(Y_Lang.trim(selector)); 56754 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) 56755 query = selector, // original query for debug report 56756 tokens = [], // array of tokens 56757 found = false, // whether or not any matches were found this pass 56758 match, // the regex match 56759 test, 56760 i, parser; 56761 56762 /* 56763 Search for selector patterns, store, and strip them from the selector string 56764 until no patterns match (invalid selector) or we run out of chars. 56765 56766 Multiple attributes and pseudos are allowed, in any order. 56767 for example:
56768 'form:first-child[type=button]:not(button)[lang|=en]' 56769 */ 56770 56771 outer: 56772 do { 56773 found = false; // reset after full pass 56774 56775 for (i = 0; (parser = Selector._parsers[i++]);) { 56776 if ( (match = parser.re.exec(selector)) ) { // note assignment 56777 if (parser.name !== COMBINATOR ) { 56778 token.selector = selector; 56779 } 56780 selector = selector.replace(match[0], ''); // strip current match from selector 56781 if (!selector.length) { 56782 token.last = true; 56783 } 56784 56785 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. 56786 match[1] = Selector._attrFilters[match[1]]; 56787 } 56788 56789 test = parser.fn(match, token); 56790 if (test === false) { // selector not supported 56791 found = false; 56792 break outer; 56793 } else if (test) { 56794 token.tests.push(test); 56795 } 56796 56797 if (!selector.length || parser.name === COMBINATOR) { 56798 tokens.push(token); 56799 token = Selector._getToken(token); 56800 if (parser.name === COMBINATOR) { 56801 token.combinator = Selector.combinators[match[1]]; 56802 } 56803 } 56804 found = true; 56805 56806 56807 } 56808 } 56809 } while (found && selector.length); 56810 56811 if (!found || selector.length) { // not fully parsed 56812 Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector'); 56813 tokens = []; 56814 } 56815 return tokens; 56816 }, 56817 56818 _replaceShorthand: function(selector) { 56819 var shorthand = Selector.shorthand, 56820 esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc. 56821 attrs, 56822 pseudos, 56823 re, i, len; 56824 56825 if (esc) { 56826 selector = selector.replace(Selector._re.esc, '\uE000'); 56827 } 56828 56829 attrs = selector.match(Selector._re.attr); 56830 pseudos = selector.match(Selector._re.pseudos); 56831 56832 if (attrs) { 56833 selector = selector.replace(Selector._re.attr, '\uE001'); 56834 } 56835 56836 if (pseudos) { 56837 selector = selector.replace(Selector._re.pseudos, '\uE002'); 56838 } 56839 56840 56841 for (re in shorthand) { 56842 if (shorthand.hasOwnProperty(re)) { 56843 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); 56844 } 56845 } 56846 56847 if (attrs) { 56848 for (i = 0, len = attrs.length; i < len; ++i) { 56849 selector = selector.replace(/\uE001/, attrs[i]); 56850 } 56851 } 56852 56853 if (pseudos) { 56854 for (i = 0, len = pseudos.length; i < len; ++i) { 56855 selector = selector.replace(/\uE002/, pseudos[i]); 56856 } 56857 } 56858 56859 selector = selector.replace(/\[/g, '\uE003'); 56860 selector = selector.replace(/\]/g, '\uE004'); 56861 56862 selector = selector.replace(/\(/g, '\uE005'); 56863 selector = selector.replace(/\)/g, '\uE006'); 56864 56865 if (esc) { 56866 for (i = 0, len = esc.length; i < len; ++i) { 56867 selector = selector.replace('\uE000', esc[i]); 56868 } 56869 } 56870 56871 return selector; 56872 }, 56873 56874 _attrFilters: { 56875 'class': 'className', 56876 'for': 'htmlFor' 56877 }, 56878 56879 getters: { 56880 href: function(node, attr) { 56881 return Y_DOM.getAttribute(node, attr); 56882 } 56883 } 56884 }; 56885 56886Y_mix(Selector, SelectorCSS2, true); 56887Selector.getters.src = Selector.getters.rel = Selector.getters.href; 56888
56889// IE wants class with native queries 56890if (Selector.useNative && Y_DOC.querySelector) { 56891 Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]'; 56892} 56893 56894/** 56895 * The selector css3 module provides support for css3 selectors. 56896 * @module dom 56897 * @submodule selector-css3 56898 * @for Selector 56899 */ 56900 56901/* 56902 an+b = get every _a_th node starting at the _b_th 56903 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element 56904 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") 56905 an+0 = get every _a_th element, "0" may be omitted 56906*/ 56907 56908Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; 56909 56910Selector._getNth = function(node, expr, tag, reverse) { 56911 Selector._reNth.test(expr); 56912 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) 56913 n = RegExp.$2, // "n" 56914 oddeven = RegExp.$3, // "odd" or "even" 56915 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ 56916 result = [], 56917 siblings = Selector._children(node.parentNode, tag), 56918 op; 56919 56920 if (oddeven) { 56921 a = 2; // always every other 56922 op = '+'; 56923 n = 'n'; 56924 b = (oddeven === 'odd') ? 1 : 0; 56925 } else if ( isNaN(a) ) { 56926 a = (n) ? 1 : 0; // start from the first or no repeat 56927 } 56928 56929 if (a === 0) { // just the first 56930 if (reverse) { 56931 b = siblings.length - b + 1; 56932 } 56933 56934 if (siblings[b - 1] === node) { 56935 return true; 56936 } else { 56937 return false; 56938 } 56939 56940 } else if (a < 0) { 56941 reverse = !!reverse; 56942 a = Math.abs(a); 56943 } 56944 56945 if (!reverse) { 56946 for (var i = b - 1, len = siblings.length; i < len; i += a) { 56947 if ( i >= 0 && siblings[i] === node ) { 56948 return true; 56949 } 56950 } 56951 } else { 56952 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { 56953 if ( i < len && siblings[i] === node ) { 56954 return true; 56955 } 56956 } 56957 } 56958 return false; 56959}; 56960 56961Y_mix(Selector.pseudos, { 56962 'root': function(node) { 56963 return node === node.ownerDocument.documentElement; 56964 }, 56965 56966 'nth-child': function(node, expr) { 56967 return Selector._getNth(node, expr); 56968 }, 56969 56970 'nth-last-child': function(node, expr) { 56971 return Selector._getNth(node, expr, null, true); 56972 }, 56973 56974 'nth-of-type': function(node, expr) { 56975 return Selector._getNth(node, expr, node.tagName); 56976 }, 56977 56978 'nth-last-of-type': function(node, expr) { 56979 return Selector._getNth(node, expr, node.tagName, true); 56980 }, 56981 56982 'last-child': function(node) { 56983 var children = Selector._children(node.parentNode); 56984 return children[children.length - 1] === node; 56985 }, 56986 56987 'first-of-type': function(node) { 56988 return Selector._children(node.parentNode, node.tagName)[0] === node; 56989 }, 56990 56991 'last-of-type': function(node) { 56992 var children = Selector._children(node.parentNode, node.tagName); 56993 return children[children.length - 1] === node; 56994 }, 56995 56996 'only-child': function(node) { 56997 var children = Selector._children(node.parentNode); 56998 return children.length === 1 && children[0] === node; 56999 }, 57000 57001 'only-of-type': function(node) { 57002 var children = Selector._children(node.parentNode, node.tagName); 57003 return children.length === 1 && children[0] === node; 57004 }, 57005 57006 'empty': function(node) { 57007 return node.childNodes.length === 0; 57008 }, 57009 57010 'not': function(node, expr) { 57011 return !Selector.test(node, expr); 57012 }, 57013 57014 'contains': function(node, expr) { 57015 var text = node.innerText || node.textContent || ''; 57016 return text.indexOf(expr) > -1; 57017 }, 57018 57019 'checked': function(node) { 57020 return (node.checked === true || node.selected === true); 57021 }, 57022 57023 enabled: function(node) { 57024 return (node.disabled !== undefined && !node.disabled); 57025 }, 57026 57027 disabled: function(node) { 57028 return (node.disabled); 57029 } 57030}); 57031 57032Y_mix(Selector.operators, { 57033 '^=': '^{val}', // Match starts with value 57034 '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value 57035 '$=': '{val}$', // Match ends with value 57036 '*=': '{val}' // Match contains value as substring 57037}); 57038 57039Selector.combinators['~'] = { 57040 axis: 'previousSibling' 57041}; 57042YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"}); 57043 57044 57045 57046/****************************************************************************/ 57047/****************************************************************************/ 57048/****************************************************************************/ 57049 57050var Dom = YAHOO.util.Dom; 57051 57052/** 57053 * The ColumnSet class defines and manages a DataTable's Columns, 57054 * including nested hierarchies and access to individual Column instances. 57055 * 57056 * @namespace YAHOO.widget 57057 * @class ColumnSet 57058 * @uses YAHOO.util.EventProvider 57059 * @constructor 57060 * @param aDefinitions {Object[]} Array of object literals that define cells in 57061 * the THEAD. 57062 */ 57063YAHOO.widget.ColumnSet = function(aDefinitions) { 57064 this._sId = Dom.generateId(null, "yui-cs"); // "yui-cs" + YAHOO.widget.ColumnSet._nCount; 57065 57066 // First clone the defs 57067 aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions); 57068 this._init(aDefinitions); 57069 57070 YAHOO.widget.ColumnSet._nCount++; 57071 YAHOO.log("ColumnSet initialized", "info", this.toString()); 57072}; 57073 57074///////////////////////////////////////////////////////////////////////////// 57075// 57076// Private member variables 57077// 57078///////////////////////////////////////////////////////////////////////////// 57079 57080/**
57081 * Internal class variable to index multiple ColumnSet instances. 57082 * 57083 * @property ColumnSet._nCount 57084 * @type Number 57085 * @private 57086 * @static 57087 */ 57088YAHOO.widget.ColumnSet._nCount = 0; 57089 57090YAHOO.widget.ColumnSet.prototype = { 57091 /** 57092 * Unique instance name. 57093 * 57094 * @property _sId 57095 * @type String 57096 * @private 57097 */ 57098 _sId : null, 57099 57100 /** 57101 * Array of object literal Column definitions passed to the constructor. 57102 * 57103 * @property _aDefinitions 57104 * @type Object[] 57105 * @private 57106 */ 57107 _aDefinitions : null, 57108 57109 ///////////////////////////////////////////////////////////////////////////// 57110 // 57111 // Public member variables 57112 // 57113 ///////////////////////////////////////////////////////////////////////////// 57114 57115 /** 57116 * Top-down tree representation of Column hierarchy. 57117 * 57118 * @property tree 57119 * @type YAHOO.widget.Column[] 57120 */ 57121 tree : null, 57122 57123 /** 57124 * Flattened representation of all Columns. 57125 * 57126 * @property flat 57127 * @type YAHOO.widget.Column[] 57128 * @default [] 57129 */ 57130 flat : null, 57131 57132 /** 57133 * Array of Columns that map one-to-one to a table column. 57134 * 57135 * @property keys 57136 * @type YAHOO.widget.Column[] 57137 * @default [] 57138 */ 57139 keys : null, 57140 57141 /** 57142 * ID index of nested parent hierarchies for HEADERS accessibility attribute. 57143 * 57144 * @property headers
57145 * @type String[] 57146 * @default [] 57147 */ 57148 headers : null, 57149 57150 ///////////////////////////////////////////////////////////////////////////// 57151 // 57152 // Private methods 57153 // 57154 ///////////////////////////////////////////////////////////////////////////// 57155 57156 /** 57157 * Initializes ColumnSet instance with data from Column definitions. 57158 * 57159 * @method _init 57160 * @param aDefinitions {Object[]} Array of object literals that define cells in 57161 * the THEAD . 57162 * @private 57163 */ 57164 57165 _init : function(aDefinitions) { 57166 // DOM tree representation of all Columns 57167 var tree = []; 57168 // Flat representation of all Columns 57169 var flat = []; 57170 // Flat representation of only Columns that are meant to display data 57171 var keys = []; 57172 // Array of HEADERS attribute values for all keys in the "keys" array 57173 var headers = []; 57174 57175 // Tracks current node list depth being tracked 57176 var nodeDepth = -1; 57177 57178 // Internal recursive function to define Column instances 57179 var parseColumns = function(nodeList, parent) { 57180 // One level down 57181 nodeDepth++; 57182 57183 // Create corresponding tree node if not already there for this depth 57184 if(!tree[nodeDepth]) { 57185 tree[nodeDepth] = []; 57186 } 57187 57188 57189 // Parse each node at this depth for attributes and any children 57190 for(var j=0; j<nodeList.length; j++) { 57191 var currentNode = nodeList[j]; 57192 57193 // Instantiate a new Column for each node 57194 var oColumn = new YAHOO.widget.Column(currentNode); 57195 57196 // Cross-reference Column ID back to the original object literal definition 57197 currentNode.yuiColumnId = oColumn._sId; 57198 57199 // Add the new Column to the flat list 57200 flat.push(oColumn); 57201 57202 // Assign its parent as an attribute, if applicable 57203 if(parent) { 57204 oColumn._oParent = parent; 57205 } 57206 57207 // The Column has descendants 57208 if(YAHOO.lang.isArray(currentNode.children)) { 57209 oColumn.children = currentNode.children; 57210 57211 // Determine COLSPAN value for this Column 57212 var terminalChildNodes = 0; 57213 var countTerminalChildNodes = function(ancestor) { 57214 var descendants = ancestor.children; 57215 // Drill down each branch and count terminal nodes 57216 for(var k=0; k<descendants.length; k++) { 57217 // Keep drilling down 57218 if(YAHOO.lang.isArray(descendants[k].children)) { 57219 countTerminalChildNodes(descendants[k]); 57220 } 57221 // Reached branch terminus 57222 else { 57223 terminalChildNodes++; 57224 } 57225 } 57226 }; 57227 countTerminalChildNodes(currentNode); 57228 oColumn._nColspan = terminalChildNodes; 57229 57230 // Cascade certain properties to children if not defined on their own 57231 var currentChildren = currentNode.children; 57232 for(var k=0; k<currentChildren.length; k++) { 57233 var child = currentChildren[k]; 57234 if(oColumn.className && (child.className === undefined)) { 57235 child.className = oColumn.className; 57236 } 57237 if(oColumn.editor && (child.editor === undefined)) { 57238 child.editor = oColumn.editor; 57239 } 57240 //TODO: Deprecated 57241 if(oColumn.editorOptions && (child.editorOptions === undefined)) { 57242 child.editorOptions = oColumn.editorOptions; 57243 } 57244 if(oColumn.formatter && (child.formatter === undefined)) { 57245 child.formatter = oColumn.formatter; 57246 }
57247 if(oColumn.resizeable && (child.resizeable === undefined)) { 57248 child.resizeable = oColumn.resizeable; 57249 } 57250 if(oColumn.sortable && (child.sortable === undefined)) { 57251 child.sortable = oColumn.sortable; 57252 } 57253 if(oColumn.hidden) { 57254 child.hidden = true; 57255 } 57256 if(oColumn.width && (child.width === undefined)) { 57257 child.width = oColumn.width; 57258 } 57259 if(oColumn.minWidth && (child.minWidth === undefined)) { 57260 child.minWidth = oColumn.minWidth; 57261 } 57262 if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) { 57263 child.maxAutoWidth = oColumn.maxAutoWidth; 57264 } 57265 // Backward compatibility 57266 if(oColumn.type && (child.type === undefined)) { 57267 child.type = oColumn.type; 57268 } 57269 if(oColumn.type && !oColumn.formatter) { 57270 YAHOO.log("The property type has been" +
57271 " deprecated in favor of formatter", "warn", oColumn.toString()); 57272 oColumn.formatter = oColumn.type; 57273 } 57274 if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) { 57275 YAHOO.log("The property text has been" + 57276 " deprecated in favor of label", "warn", oColumn.toString()); 57277 oColumn.label = oColumn.text; 57278 } 57279 if(oColumn.parser) { 57280 YAHOO.log("The property parser is no longer supported", 57281 "warn", this.toString()); 57282 } 57283 if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) || 57284 (oColumn.sortOptions.descFunction))) { 57285 YAHOO.log("The properties sortOptions.ascFunction and " + 57286 " sortOptions.descFunction have been deprecated in favor " + 57287 " of sortOptions.sortFunction", "warn", oColumn.toString()); 57288 } 57289 } 57290 57291 // The children themselves must also be parsed for Column instances 57292 if(!tree[nodeDepth+1]) { 57293 tree[nodeDepth+1] = []; 57294 } 57295 parseColumns(currentChildren, oColumn); 57296 } 57297 // This Column does not have any children 57298 else { 57299 oColumn._nKeyIndex = keys.length; 57300 oColumn._nColspan = 1; 57301 keys.push(oColumn); 57302 } 57303 57304 // Add the Column to the top-down tree 57305 tree[nodeDepth].push(oColumn); 57306 } 57307 nodeDepth--; 57308 }; 57309 57310 // Parse out Column instances from the array of object literals 57311 if(YAHOO.lang.isArray(aDefinitions)) { 57312 parseColumns(aDefinitions); 57313 57314 // Store the array 57315 this._aDefinitions = aDefinitions; 57316 } 57317 else { 57318 YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error"); 57319 return null; 57320 } 57321 57322 var i; 57323 57324 // Determine ROWSPAN value for each Column in the tree 57325 var parseTreeForRowspan = function(tree) { 57326 var maxRowDepth = 1; 57327 var currentRow; 57328 var currentColumn; 57329 57330 // Calculate the max depth of descendants for this row 57331 var countMaxRowDepth = function(row, tmpRowDepth) { 57332 tmpRowDepth = tmpRowDepth || 1; 57333 57334 for(var n=0; n<row.length; n++) { 57335 var col = row[n]; 57336 // Column has children, so keep counting 57337 if(YAHOO.lang.isArray(col.children)) { 57338 tmpRowDepth++; 57339 countMaxRowDepth(col.children, tmpRowDepth); 57340 tmpRowDepth--; 57341 } 57342 // No children, is it the max depth? 57343 else { 57344 if(tmpRowDepth > maxRowDepth) { 57345 maxRowDepth = tmpRowDepth; 57346 } 57347 } 57348 57349 } 57350 }; 57351 57352 // Count max row depth for each row 57353 for(var m=0; m<tree.length; m++) { 57354 currentRow = tree[m]; 57355 countMaxRowDepth(currentRow); 57356 57357 // Assign the right ROWSPAN values to each Column in the row 57358 for(var p=0; p<currentRow.length; p++) { 57359 currentColumn = currentRow[p]; 57360 if(!YAHOO.lang.isArray(currentColumn.children)) { 57361 currentColumn._nRowspan = maxRowDepth; 57362 } 57363 else { 57364 currentColumn._nRowspan = 1; 57365 } 57366 } 57367 57368 // Reset counter for next row 57369 maxRowDepth = 1; 57370 } 57371 }; 57372 parseTreeForRowspan(tree); 57373 57374 // Store tree index values 57375 for(i=0; i<tree[0].length; i++) { 57376 tree[0][i]._nTreeIndex = i; 57377 } 57378 57379 // Store header relationships in an array for HEADERS attribute 57380 var recurseAncestorsForHeaders = function(i, oColumn) { 57381 headers[i].push(oColumn.getSanitizedKey()); 57382 if(oColumn._oParent) { 57383 recurseAncestorsForHeaders(i, oColumn._oParent); 57384 } 57385 }; 57386 for(i=0; i<keys.length; i++) { 57387 headers[i] = []; 57388 recurseAncestorsForHeaders(i, keys[i]); 57389 headers[i] = headers[i].reverse(); 57390 } 57391 57392 // Save to the ColumnSet instance 57393 this.tree = tree; 57394 this.flat = flat; 57395 this.keys = keys; 57396 this.headers = headers; 57397 }, 57398
57399 ///////////////////////////////////////////////////////////////////////////// 57400 // 57401 // Public methods 57402 // 57403 ///////////////////////////////////////////////////////////////////////////// 57404 57405 /** 57406 * Returns unique name of the ColumnSet instance. 57407 * 57408 * @method getId 57409 * @return {String} Unique name of the ColumnSet instance. 57410 */ 57411 57412 getId : function() { 57413 return this._sId; 57414 }, 57415 57416 /** 57417 * ColumnSet instance name, for logging. 57418 * 57419 * @method toString 57420 * @return {String} Unique name of the ColumnSet instance. 57421 */ 57422 57423 toString : function() { 57424 return "ColumnSet instance " + this._sId; 57425 }, 57426 57427 /** 57428 * Public accessor to the definitions array. 57429 * 57430 * @method getDefinitions 57431 * @return {Object[]} Array of object literal Column definitions. 57432 */ 57433 57434 getDefinitions : function() { 57435 var aDefinitions = this._aDefinitions; 57436 57437 // Internal recursive function to define Column instances 57438 var parseColumns = function(nodeList, oSelf) { 57439 // Parse each node at this depth for attributes and any children 57440 for(var j=0; j<nodeList.length; j++) { 57441 var currentNode = nodeList[j]; 57442 57443 // Get the Column for each node 57444 var oColumn = oSelf.getColumnById(currentNode.yuiColumnId); 57445 57446 if(oColumn) { 57447 // Update the current values 57448 var oDefinition = oColumn.getDefinition(); 57449 for(var name in oDefinition) { 57450 if(YAHOO.lang.hasOwnProperty(oDefinition, name)) { 57451 currentNode[name] = oDefinition[name]; 57452 } 57453 } 57454 } 57455 57456 // The Column has descendants 57457 if(YAHOO.lang.isArray(currentNode.children)) { 57458 // The children themselves must also be parsed for Column instances 57459 parseColumns(currentNode.children, oSelf); 57460 } 57461 } 57462 }; 57463 57464 parseColumns(aDefinitions, this); 57465 this._aDefinitions = aDefinitions; 57466 return aDefinitions; 57467 }, 57468 57469 /** 57470 * Returns Column instance with given ID. 57471 * 57472 * @method getColumnById 57473 * @param column {String} Column ID. 57474 * @return {YAHOO.widget.Column} Column instance. 57475 */ 57476 57477 getColumnById : function(column) { 57478 if(YAHOO.lang.isString(column)) { 57479 var allColumns = this.flat; 57480 for(var i=allColumns.length-1; i>-1; i--) { 57481 if(allColumns[i]._sId === column) { 57482 return allColumns[i]; 57483 } 57484 } 57485 } 57486 return null; 57487 }, 57488 57489 /** 57490 * Returns Column instance with given key or ColumnSet key index. 57491 * 57492 * @method getColumn 57493 * @param column {String | Number} Column key or ColumnSet key index. 57494 * @return {YAHOO.widget.Column} Column instance. 57495 */ 57496 57497 getColumn : function(column) { 57498 if(YAHOO.lang.isNumber(column) && this.keys[column]) { 57499 return this.keys[column]; 57500 } 57501 else if(YAHOO.lang.isString(column)) { 57502 var allColumns = this.flat; 57503 var aColumns = []; 57504 for(var i=0; i<allColumns.length; i++) { 57505 if(allColumns[i].key === column) { 57506 aColumns.push(allColumns[i]); 57507 } 57508 } 57509 if(aColumns.length === 1) { 57510 return aColumns[0]; 57511 } 57512 else if(aColumns.length > 1) { 57513 return aColumns; 57514 } 57515 } 57516 return null; 57517 }, 57518 57519 /** 57520 * Public accessor returns array of given Column's desendants (if any), including itself. 57521 * 57522 * @method getDescendants 57523 * @parem {YAHOO.widget.Column} Column instance. 57524 * @return {Array} Array including the Column itself and all descendants (if any). 57525 */ 57526 getDescendants : function(oColumn) { 57527 var oSelf = this; 57528 var allDescendants = []; 57529 var i; 57530 57531 // Recursive function to loop thru all children 57532 var parse = function(oParent) { 57533 allDescendants.push(oParent); 57534 // This Column has children 57535 if(oParent.children) { 57536 for(i=0; i<oParent.children.length; i++) { 57537 parse(oSelf.getColumn(oParent.children[i].key)); 57538 } 57539 } 57540 }; 57541 parse(oColumn); 57542 57543 return allDescendants; 57544 } 57545}; 57546 57547/****************************************************************************/ 57548/****************************************************************************/ 57549/****************************************************************************/ 57550 57551/** 57552 * The Column class defines and manages attributes of DataTable Columns 57553 * 57554 * @namespace YAHOO.widget 57555 * @class Column 57556 * @constructor 57557 * @param oConfigs {Object} Object literal of definitions. 57558 */ 57559YAHOO.widget.Column = function(oConfigs) { 57560 this._sId = Dom.generateId(null, "yui-col"); // "yui-col" + YAHOO.widget.Column._nCount; 57561 57562 // Object literal defines Column attributes 57563 if(oConfigs && YAHOO.lang.isObject(oConfigs)) { 57564 for(var sConfig in oConfigs) { 57565 if(sConfig) { 57566 this[sConfig] = oConfigs[sConfig]; 57567 } 57568 } 57569 } 57570 57571 // Assign a key if not found 57572 if(!YAHOO.lang.isValue(this.key)) { 57573 this.key = Dom.generateId(null, "yui-dt-col"); //"yui-dt-col" + YAHOO.widget.Column._nCount; 57574 } 57575 57576 // Assign a field if not found, defaults to key 57577 if(!YAHOO.lang.isValue(this.field)) { 57578 this.field = this.key; 57579 } 57580 57581 // Increment counter 57582 YAHOO.widget.Column._nCount++; 57583 57584 // Backward compatibility 57585 if(this.width && !YAHOO.lang.isNumber(this.width)) { 57586 this.width = null; 57587 YAHOO.log("The Column property width must be a number", "warn", this.toString()); 57588 } 57589 if(this.editor && YAHOO.lang.isString(this.editor)) { 57590 this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions); 57591 YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString()); 57592 } 57593}; 57594 57595///////////////////////////////////////////////////////////////////////////// 57596// 57597// Private member variables 57598// 57599///////////////////////////////////////////////////////////////////////////// 57600 57601YAHOO.lang.augmentObject(YAHOO.widget.Column, { 57602 /**
57603 * Internal class variable to index multiple Column instances. 57604 * 57605 * @property Column._nCount 57606 * @type Number 57607 * @private 57608 * @static 57609 */ 57610 _nCount : 0, 57611 57612 formatCheckbox : function(elCell, oRecord, oColumn, oData) { 57613 YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" + 57614 " deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn", 57615 "YAHOO.widget.Column.formatCheckbox"); 57616 YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData); 57617 }, 57618 57619 formatCurrency : function(elCell, oRecord, oColumn, oData) { 57620 YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" + 57621 " deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn", 57622 "YAHOO.widget.Column.formatCurrency"); 57623 YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData); 57624 }, 57625 57626 formatDate : function(elCell, oRecord, oColumn, oData) { 57627 YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
57628 " deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn", 57629 "YAHOO.widget.Column.formatDate"); 57630 YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData); 57631 }, 57632 57633 formatEmail : function(elCell, oRecord, oColumn, oData) { 57634 YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" + 57635 " deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn", 57636 "YAHOO.widget.Column.formatEmail"); 57637 YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData); 57638 }, 57639 57640 formatLink : function(elCell, oRecord, oColumn, oData) { 57641 YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" + 57642 " deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn", 57643 "YAHOO.widget.Column.formatLink"); 57644 YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData); 57645 }, 57646 57647 formatNumber : function(elCell, oRecord, oColumn, oData) { 57648 YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
57649 " deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn", 57650 "YAHOO.widget.Column.formatNumber"); 57651 YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData); 57652 }, 57653 57654 formatSelect : function(elCell, oRecord, oColumn, oData) { 57655 YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" + 57656 " deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn", 57657 "YAHOO.widget.Column.formatSelect"); 57658 YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData); 57659 } 57660}); 57661 57662YAHOO.widget.Column.prototype = { 57663 /** 57664 * Unique String identifier assigned at instantiation. 57665 * 57666 * @property _sId 57667 * @type String 57668 * @private 57669 */ 57670 _sId : null, 57671 57672 /** 57673 * Reference to Column's current position index within its ColumnSet's keys 57674 * array, if applicable. This property only applies to non-nested and bottom- 57675 * level child Columns. 57676 * 57677 * @property _nKeyIndex 57678 * @type Number 57679 * @private 57680 */ 57681 _nKeyIndex : null, 57682 57683 /** 57684 * Reference to Column's current position index within its ColumnSet's tree 57685 * array, if applicable. This property only applies to non-nested and top- 57686 * level parent Columns. 57687 * 57688 * @property _nTreeIndex 57689 * @type Number 57690 * @private 57691 */ 57692 _nTreeIndex : null, 57693 57694 /** 57695 * Number of table cells the Column spans. 57696 * 57697 * @property _nColspan 57698 * @type Number 57699 * @private 57700 */ 57701 _nColspan : 1, 57702 57703 /** 57704 * Number of table rows the Column spans. 57705 * 57706 * @property _nRowspan 57707 * @type Number 57708 * @private 57709 */ 57710 _nRowspan : 1, 57711 57712 /** 57713 * Column's parent Column instance, or null. 57714 * 57715 * @property _oParent 57716 * @type YAHOO.widget.Column 57717 * @private 57718 */ 57719 _oParent : null, 57720 57721 /** 57722 * The DOM reference to the associated TH element. 57723 * 57724 * @property _elTh 57725 * @type HTMLElement 57726 * @private 57727 */ 57728 _elTh : null, 57729 57730 /** 57731 * The DOM reference to the associated TH element's liner DIV element. 57732 * 57733 * @property _elThLiner 57734 * @type HTMLElement 57735 * @private 57736 */ 57737 _elThLiner : null, 57738 57739 /** 57740 * The DOM reference to the associated TH element's label SPAN element. 57741 * 57742 * @property _elThLabel 57743 * @type HTMLElement 57744 * @private 57745 */ 57746 _elThLabel : null, 57747 57748 /** 57749 * The DOM reference to the associated resizerelement (if any). 57750 * 57751 * @property _elResizer 57752 * @type HTMLElement 57753 * @private 57754 */ 57755 _elResizer : null, 57756 57757 /** 57758 * Internal width tracker. 57759 * 57760 * @property _nWidth 57761 * @type Number 57762 * @private 57763 */ 57764 _nWidth : null, 57765 57766 /** 57767 * For unreg() purposes, a reference to the Column's DragDrop instance. 57768 * 57769 * @property _dd 57770 * @type YAHOO.util.DragDrop 57771 * @private 57772 */ 57773 _dd : null, 57774 57775 /** 57776 * For unreg() purposes, a reference to the Column resizer's DragDrop instance. 57777 * 57778 * @property _ddResizer 57779 * @type YAHOO.util.DragDrop 57780 * @private 57781 */ 57782 _ddResizer : null, 57783 57784 ///////////////////////////////////////////////////////////////////////////// 57785 // 57786 // Public member variables 57787 // 57788 ///////////////////////////////////////////////////////////////////////////// 57789 57790 /** 57791 * Unique name, required. If "label" property is not provided, the "key" 57792 * value will be treated as markup and inserted into the DOM as innerHTML. 57793 * 57794 * @property key
57795 * @type String|HTML 57796 */ 57797 key : null, 57798 57799 /** 57800 * Associated database field, or null. 57801 * 57802 * @property field 57803 * @type String 57804 */ 57805 field : null, 57806 57807 /** 57808 * Value displayed as Column header in the TH element. String value is 57809 * treated as markup and inserted into the DOM as innerHTML. 57810 * 57811 * @property label 57812 * @type HTML 57813 */ 57814 label : null, 57815 57816 /** 57817 * Column head cell ABBR for accessibility. 57818 * 57819 * @property abbr 57820 * @type String 57821 */ 57822 abbr : null, 57823 57824 /** 57825 * Array of object literals that define children (nested headers) of a Column. 57826 * 57827 * @property children 57828 * @type Object[] 57829 */ 57830 children : null, 57831 57832 /** 57833 * Column width (in pixels). 57834 * 57835 * @property width 57836 * @type Number 57837 */ 57838 width : null, 57839 57840 /** 57841 * Minimum Column width (in pixels). 57842 * 57843 * @property minWidth 57844 * @type Number 57845 * @default null 57846 */ 57847 minWidth : null, 57848 57849 /** 57850 * When a width is not defined for a Column, maxAutoWidth defines an upper 57851 * limit that the Column should be auto-sized to. If resizeable is enabled, 57852 * users may still resize to a greater width. Most useful for Columns intended 57853 * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very 57854 * wide Columns from disrupting visual readability by inducing truncation. 57855 * 57856 * @property maxAutoWidth 57857 * @type Number 57858 * @default null 57859 */ 57860 maxAutoWidth : null, 57861 57862 /** 57863 * True if Column is in hidden state. 57864 * 57865 * @property hidden 57866 * @type Boolean 57867 * @default false 57868 */ 57869 hidden : false, 57870 57871 /** 57872 * True if Column is in selected state. 57873 * 57874 * @property selected 57875 * @type Boolean 57876 * @default false 57877 */ 57878 selected : false, 57879 57880 /** 57881 * Custom CSS class or array of classes to be applied to every cell in the Column. 57882 * 57883 * @property className
57884 * @type String || String[] 57885 */ 57886 className : null, 57887 57888 /** 57889 * Cell formatter function, or a shortcut pointer to a function in the 57890 * DataTable.Formatter object. The function, called from the DataTable's 57891 * formatCell method, renders markup into the cell liner 57892 * element and accepts the following arguments: 57893 * <dl> 57894 * <dt>elLiner</dt> 57895 * <dd>The element to write innerHTML to.</dd> 57896 * <dt>oRecord</dt> 57897 * <dd>The associated Record for the row.</dd> 57898 * <dt>oColumn</dt> 57899 * <dd>The Column instance for the cell.</dd> 57900 * <dt>oData</dt> 57901 * <dd>The data value for the cell.</dd> 57902 * </dl> 57903 * 57904 * @property formatter 57905 * @type String || HTMLFunction 57906 */ 57907 formatter : null, 57908 57909 /** 57910 * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter. 57911 * 57912 * @property currencyOptions 57913 * @type Object 57914 * @default null 57915 */ 57916 currencyOptions : null, 57917 57918 /** 57919 * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter. 57920 * 57921 * @property dateOptions 57922 * @type Object 57923 * @default null 57924 */ 57925 dateOptions : null, 57926 57927 /** 57928 * Array of dropdown values for formatter:"dropdown" cases. Can either be a 57929 * simple array (e.g., ["Alabama","Alaska","Arizona","Arkansas"]) or a an 57930 * array of objects (e.g., [{label:"Alabama", value:"AL"}, 57931 * {label:"Alaska", value:"AK"}, {label:"Arizona", value:"AZ"}, 57932 * {label:"Arkansas", value:"AR"}]). String values are treated as markup and 57933 * inserted into the DOM as innerHTML. 57934 * 57935 * @property dropdownOptions 57936 * @type HTML[] | Object[] 57937 */ 57938 dropdownOptions : null, 57939 57940 /** 57941 * A CellEditor instance, otherwise Column is not editable. 57942 * 57943 * @property editor 57944 * @type YAHOO.widget.CellEditor 57945 */ 57946 editor : null, 57947 57948 /** 57949 * True if Column is resizeable, false otherwise. The Drag & Drop Utility is 57950 * required to enable this feature. Only bottom-level and non-nested Columns are 57951 * resizeble. 57952 * 57953 * @property resizeable 57954 * @type Boolean 57955 * @default false 57956 */ 57957 resizeable : false, 57958 57959 /** 57960 * True if Column is sortable, false otherwise. 57961 * 57962 * @property sortable 57963 * @type Boolean 57964 * @default false 57965 */ 57966 sortable : false, 57967 57968 /** 57969 * @property sortOptions.defaultOrder 57970 * @deprecated Use sortOptions.defaultDir. 57971 */ 57972 /** 57973 * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC. 57974 * 57975 * @property sortOptions.defaultDir
57976 * @type String 57977 * @default null 57978 */ 57979 /** 57980 * Custom field to sort on. 57981 * 57982 * @property sortOptions.field 57983 * @type String 57984 * @default null 57985 */ 57986 /** 57987 * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value 57988 * 57989 * @property sortOptions.sortFunction 57990 * @type Function 57991 * @default null 57992 */ 57993 sortOptions : null, 57994 57995 57996 57997 57998 57999 58000 58001 58002 58003 58004 58005 58006 58007 58008 58009 ///////////////////////////////////////////////////////////////////////////// 58010 // 58011 // Public methods 58012 // 58013 ///////////////////////////////////////////////////////////////////////////// 58014 58015 /** 58016 * Returns unique ID string. 58017 * 58018 * @method getId 58019 * @return {String} Unique ID string. 58020 */ 58021 getId : function() { 58022 return this._sId; 58023 }, 58024 58025 /** 58026 * Column instance name, for logging. 58027 * 58028 * @method toString 58029 * @return {String} Column's unique name. 58030 */ 58031 toString : function() { 58032 return "Column instance " + this._sId; 58033 }, 58034 58035 /** 58036 * Returns object literal definition. 58037 * 58038 * @method getDefinition 58039 * @return {Object} Object literal definition. 58040 */ 58041 getDefinition : function() { 58042 var oDefinition = {}; 58043 58044 // Update the definition 58045 oDefinition.abbr = this.abbr; 58046 oDefinition.className = this.className; 58047 oDefinition.editor = this.editor; 58048 oDefinition.editorOptions = this.editorOptions; //TODO: deprecated 58049 oDefinition.field = this.field; 58050 oDefinition.formatter = this.formatter; 58051 oDefinition.hidden = this.hidden; 58052 oDefinition.key = this.key; 58053 oDefinition.label = this.label; 58054 oDefinition.minWidth = this.minWidth; 58055 oDefinition.maxAutoWidth = this.maxAutoWidth; 58056 oDefinition.resizeable = this.resizeable; 58057 oDefinition.selected = this.selected; 58058 oDefinition.sortable = this.sortable; 58059 oDefinition.sortOptions = this.sortOptions; 58060 oDefinition.width = this.width; 58061 58062 // Bug 2529147 58063 oDefinition._calculatedWidth = this._calculatedWidth; 58064 58065 return oDefinition; 58066 }, 58067 58068 /** 58069 * Returns unique Column key. 58070 * 58071 * @method getKey 58072 * @return {String} Column key. 58073 */ 58074 getKey : function() { 58075 return this.key; 58076 }, 58077 58078 /** 58079 * Returns field. 58080 * 58081 * @method getField 58082 * @return {String} Column field. 58083 */ 58084 getField : function() { 58085 return this.field; 58086 }, 58087 58088 /** 58089 * Returns Column key which has been sanitized for DOM (class and ID) usage 58090 * starts with letter, contains only letters, numbers, hyphen, or period. 58091 * 58092 * @method getSanitizedKey 58093 * @return {String} Sanitized Column key. 58094 */ 58095 getSanitizedKey : function() { 58096 return this.getKey().replace(/[^\w\-]/g,""); 58097 }, 58098 58099 /** 58100 * Public accessor returns Column's current position index within its 58101 * ColumnSet's keys array, if applicable. Only non-nested and bottom-level 58102 * child Columns will return a value. 58103 * 58104 * @method getKeyIndex 58105 * @return {Number} Position index, or null. 58106 */ 58107 getKeyIndex : function() { 58108 return this._nKeyIndex; 58109 }, 58110 58111 /** 58112 * Public accessor returns Column's current position index within its 58113 * ColumnSet's tree array, if applicable. Only non-nested and top-level parent 58114 * Columns will return a value; 58115 * 58116 * @method getTreeIndex 58117 * @return {Number} Position index, or null. 58118 */ 58119 getTreeIndex : function() { 58120 return this._nTreeIndex; 58121 }, 58122 58123 /** 58124 * Public accessor returns Column's parent instance if any, or null otherwise. 58125 * 58126 * @method getParent 58127 * @return {YAHOO.widget.Column} Column's parent instance. 58128 */ 58129 getParent : function() { 58130 return this._oParent; 58131 }, 58132 58133 /** 58134 * Public accessor returns Column's calculated COLSPAN value. 58135 * 58136 * @method getColspan 58137 * @return {Number} Column's COLSPAN value. 58138 */ 58139 getColspan : function() { 58140 return this._nColspan; 58141 }, 58142 // Backward compatibility 58143 getColSpan : function() { 58144 YAHOO.log("The method getColSpan() has been" +
58145 " deprecated in favor of getColspan()", "warn", this.toString()); 58146 return this.getColspan(); 58147 }, 58148 58149 /** 58150 * Public accessor returns Column's calculated ROWSPAN value. 58151 * 58152 * @method getRowspan 58153 * @return {Number} Column's ROWSPAN value. 58154 */ 58155 getRowspan : function() { 58156 return this._nRowspan; 58157 }, 58158 58159 /** 58160 * Returns DOM reference to the key TH element. 58161 * 58162 * @method getThEl 58163 * @return {HTMLElement} TH element. 58164 */ 58165 getThEl : function() { 58166 return this._elTh; 58167 }, 58168 58169 /** 58170 * Returns DOM reference to the TH's liner DIV element. Introduced since 58171 * resizeable Columns may have an extra resizer liner, making the DIV liner 58172 * not reliably the TH element's first child. 58173 * 58174 * @method getThLInerEl 58175 * @return {HTMLElement} TH element. 58176 */ 58177 getThLinerEl : function() { 58178 return this._elThLiner; 58179 }, 58180 58181 /** 58182 * Returns DOM reference to the resizer element, or null. 58183 * 58184 * @method getResizerEl 58185 * @return {HTMLElement} DIV element. 58186 */ 58187 getResizerEl : function() { 58188 return this._elResizer; 58189 }, 58190 58191 // Backward compatibility 58192 /** 58193 * @method getColEl 58194 * @deprecated Use getThEl 58195 */ 58196 getColEl : function() { 58197 YAHOO.log("The method getColEl() has been" +
58198 " deprecated in favor of getThEl()", "warn", 58199 this.toString()); 58200 return this.getThEl(); 58201 }, 58202 getIndex : function() { 58203 YAHOO.log("The method getIndex() has been" + 58204 " deprecated in favor of getKeyIndex()", "warn", 58205 this.toString()); 58206 return this.getKeyIndex(); 58207 }, 58208 format : function() { 58209 YAHOO.log("The method format() has been deprecated in favor of the " + 58210 "DataTable method formatCell()", "error", this.toString()); 58211 } 58212}; 58213 58214/****************************************************************************/ 58215/****************************************************************************/ 58216/****************************************************************************/ 58217 58218/** 58219 * Sort static utility to support Column sorting. 58220 * 58221 * @namespace YAHOO.util 58222 * @class Sort 58223 * @static 58224 */ 58225YAHOO.util.Sort = { 58226 ///////////////////////////////////////////////////////////////////////////// 58227 // 58228 // Public methods 58229 // 58230 ///////////////////////////////////////////////////////////////////////////// 58231 58232 /** 58233 * Comparator function for simple case-insensitive string sorting. 58234 * 58235 * @method compare 58236 * @param a {Object} First sort argument. 58237 * @param b {Object} Second sort argument. 58238 * @param desc {Boolean} True if sort direction is descending, false if 58239 * sort direction is ascending. 58240 * @return {Boolean} Return -1 when a < b. Return 0 when a = b. 58241 * Return 1 when a > b. 58242 */ 58243 compare: function(a, b, desc) { 58244 if((a === null) || (typeof a == "undefined")) { 58245 if((b === null) || (typeof b == "undefined")) { 58246 return 0; 58247 } 58248 else { 58249 return 1; 58250 } 58251 } 58252 else if((b === null) || (typeof b == "undefined")) { 58253 return -1; 58254 } 58255 58256 if(a.constructor == String) { 58257 a = a.toLowerCase(); 58258 } 58259 if(b.constructor == String) { 58260 b = b.toLowerCase(); 58261 } 58262 if(a < b) { 58263 return (desc) ? 1 : -1; 58264 } 58265 else if (a > b) { 58266 return (desc) ? -1 : 1; 58267 } 58268 else { 58269 return 0; 58270 } 58271 } 58272}; 58273 58274/****************************************************************************/ 58275/****************************************************************************/ 58276/****************************************************************************/ 58277 58278/** 58279 * ColumnDD subclasses DragDrop to support rearrangeable Columns. 58280 * 58281 * @namespace YAHOO.util 58282 * @class ColumnDD 58283 * @extends YAHOO.util.DDProxy 58284 * @constructor 58285 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 58286 * @param oColumn {YAHOO.widget.Column} Column instance. 58287 * @param elTh {HTMLElement} TH element reference. 58288 * @param elTarget {HTMLElement} Drag target element. 58289 */
58290YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) { 58291 if(oDataTable && oColumn && elTh && elTarget) { 58292 this.datatable = oDataTable; 58293 this.table = oDataTable.getTableEl(); 58294 this.column = oColumn; 58295 this.headCell = elTh; 58296 this.pointer = elTarget; 58297 this.newIndex = null; 58298 this.init(elTh); 58299 this.initFrame(); // Needed for DDProxy 58300 this.invalidHandleTypes = {}; 58301 58302 // Set top/bottom padding to account for children of nested columns 58303 this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0); 58304 58305 YAHOO.util.Event.on(window, 'resize', function() { 58306 this.initConstraints(); 58307 }, this, true); 58308 } 58309 else { 58310 YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString()); 58311 } 58312}; 58313 58314if(YAHOO.util.DDProxy) { 58315 YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, { 58316 initConstraints: function() { 58317 //Get the top, right, bottom and left positions 58318 var region = YAHOO.util.Dom.getRegion(this.table), 58319 //Get the element we are working on 58320 el = this.getEl(), 58321 //Get the xy position of it 58322 xy = YAHOO.util.Dom.getXY(el), 58323 //Get the width and height 58324 width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10), 58325 height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10), 58326 //Set left to x minus left 58327 left = ((xy[0] - region.left) + 15), //Buffer of 15px 58328 //Set right to right minus x minus width 58329 right = ((region.right - xy[0] - width) + 15); 58330 58331 //Set the constraints based on the above calculations 58332 this.setXConstraint(left, right); 58333 this.setYConstraint(10, 10); 58334 }, 58335 _resizeProxy: function() { 58336 YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments); 58337 var dragEl = this.getDragEl(), 58338 el = this.getEl(); 58339 58340 YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px'); 58341 YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block'); 58342 var xy = YAHOO.util.Dom.getXY(el); 58343 YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]); 58344 58345 YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px"); 58346 YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px'); 58347 YAHOO.util.Dom.setXY(this.dragEl, xy); 58348 }, 58349 onMouseDown: function() { 58350 this.initConstraints(); 58351 this.resetConstraints(); 58352 }, 58353 clickValidator: function(e) { 58354 if(!this.column.hidden) { 58355 var target = YAHOO.util.Event.getTarget(e); 58356 return ( this.isValidHandleChild(target) && 58357 (this.id == this.handleElId || 58358 this.DDM.handleWasClicked(target, this.id)) ); 58359 } 58360 }, 58361 onDragOver: function(ev, id) { 58362 // Validate target as a Column 58363 var target = this.datatable.getColumn(id); 58364 if(target) { 58365 // Validate target as a top-level parent 58366 var targetIndex = target.getTreeIndex(); 58367 while((targetIndex === null) && target.getParent()) { 58368 target = target.getParent(); 58369 targetIndex = target.getTreeIndex(); 58370 } 58371 if(targetIndex !== null) { 58372 // Are we placing to left or right of target? 58373 var elTarget = target.getThEl(); 58374 var newIndex = targetIndex; 58375 var mouseX = YAHOO.util.Event.getPageX(ev), 58376 targetX = YAHOO.util.Dom.getX(elTarget), 58377 midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2), 58378 currentIndex = this.column.getTreeIndex(); 58379 58380 if (mouseX < midX) { 58381 YAHOO.util.Dom.setX(this.pointer, targetX); 58382 } else { 58383 var targetWidth = parseInt(elTarget.offsetWidth, 10); 58384 YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth)); 58385 newIndex++; 58386 } 58387 if (targetIndex > currentIndex) { 58388 newIndex--; 58389 } 58390 if(newIndex < 0) { 58391 newIndex = 0; 58392 } 58393 else if(newIndex > this.datatable.getColumnSet().tree[0].length) { 58394 newIndex = this.datatable.getColumnSet().tree[0].length; 58395 } 58396 this.newIndex = newIndex; 58397 } 58398 } 58399 }, 58400 onDragDrop: function() { 58401 this.datatable.reorderColumn(this.column, this.newIndex); 58402 }, 58403 endDrag: function() { 58404 this.newIndex = null; 58405 YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none'); 58406 } 58407 }); 58408} 58409 58410/****************************************************************************/ 58411/****************************************************************************/ 58412/****************************************************************************/ 58413 58414/** 58415 * ColumnResizer subclasses DragDrop to support resizeable Columns. 58416 * 58417 * @namespace YAHOO.util 58418 * @class ColumnResizer 58419 * @extends YAHOO.util.DDProxy 58420 * @constructor 58421 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 58422 * @param oColumn {YAHOO.widget.Column} Column instance. 58423 * @param elTh {HTMLElement} TH element reference. 58424 * @param sHandleElId {String} DOM ID of the handle element that causes the resize. 58425 * @param elProxy {HTMLElement} Resizer proxy element. 58426 */
58427YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) { 58428 if(oDataTable && oColumn && elTh && sHandleId) { 58429 this.datatable = oDataTable; 58430 this.column = oColumn; 58431 this.headCell = elTh; 58432 this.headCellLiner = oColumn.getThLinerEl(); 58433 this.resizerLiner = elTh.firstChild; 58434 this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id}); 58435 this.initFrame(); // Needed for proxy 58436 this.resetResizerEl(); // Needed when rowspan > 0 58437 58438 // Set right padding for bug 1858462 58439 this.setPadding(0, 1, 0, 0); 58440 } 58441 else { 58442 YAHOO.log("Column resizer could not be created","warn",oDataTable.toString()); 58443 } 58444}; 58445 58446if(YAHOO.util.DD) { 58447 YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, { 58448 ///////////////////////////////////////////////////////////////////////////// 58449 // 58450 // Public methods 58451 // 58452 ///////////////////////////////////////////////////////////////////////////// 58453 /** 58454 * Resets resizer element. 58455 * 58456 * @method resetResizerEl 58457 */ 58458 resetResizerEl : function() { 58459 var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style; 58460 resizerStyle.left = "auto"; 58461 resizerStyle.right = 0; 58462 resizerStyle.top = "auto"; 58463 resizerStyle.bottom = 0; 58464 resizerStyle.height = this.headCell.offsetHeight+"px"; 58465 }, 58466
58467 ///////////////////////////////////////////////////////////////////////////// 58468 // 58469 // Public DOM event handlers 58470 // 58471 ///////////////////////////////////////////////////////////////////////////// 58472 58473 /** 58474 * Handles mouseup events on the Column resizer. 58475 * 58476 * @method onMouseUp 58477 * @param e {string} The mouseup event 58478 */ 58479 onMouseUp : function(e) { 58480 // Reset height of all resizer els in case TH's have changed height 58481 var allKeys = this.datatable.getColumnSet().keys, 58482 col; 58483 for(var i=0, len=allKeys.length; i<len; i++) { 58484 col = allKeys[i]; 58485 if(col._ddResizer) { 58486 col._ddResizer.resetResizerEl(); 58487 } 58488 } 58489 this.resetResizerEl(); 58490 58491 var el = this.headCellLiner; 58492 var newWidth = el.offsetWidth - 58493 (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) - 58494 (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0); 58495 58496 this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth}); 58497 }, 58498 58499 /** 58500 * Handles mousedown events on the Column resizer. 58501 * 58502 * @method onMouseDown 58503 * @param e {string} The mousedown event 58504 */ 58505 onMouseDown : function(e) { 58506 this.startWidth = this.headCellLiner.offsetWidth; 58507 this.startX = YAHOO.util.Event.getXY(e)[0]; 58508 this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) + 58509 (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0); 58510 }, 58511 58512 /** 58513 * Custom clickValidator to ensure Column is not in hidden state. 58514 * 58515 * @method clickValidator 58516 * @param {Event} e 58517 * @private 58518 */ 58519 clickValidator : function(e) { 58520 if(!this.column.hidden) { 58521 var target = YAHOO.util.Event.getTarget(e); 58522 return ( this.isValidHandleChild(target) && 58523 (this.id == this.handleElId || 58524 this.DDM.handleWasClicked(target, this.id)) ); 58525 } 58526 }, 58527 58528 /** 58529 * Handles start drag on the Column resizer. 58530 * 58531 * @method startDrag 58532 * @param e {string} The drag event 58533 */ 58534 startDrag : function() { 58535 // Shrinks height of all resizer els to not hold open TH els 58536 var allKeys = this.datatable.getColumnSet().keys, 58537 thisKey = this.column.getKeyIndex(), 58538 col; 58539 for(var i=0, len=allKeys.length; i<len; i++) { 58540 col = allKeys[i]; 58541 if(col._ddResizer) { 58542 YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em"; 58543 } 58544 } 58545 }, 58546 58547 /** 58548 * Handles drag events on the Column resizer. 58549 * 58550 * @method onDrag 58551 * @param e {string} The drag event 58552 */ 58553 onDrag : function(e) { 58554 var newX = YAHOO.util.Event.getXY(e)[0]; 58555 if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) { 58556 var offsetX = newX - this.startX; 58557 var newWidth = this.startWidth + offsetX - this.nLinerPadding; 58558 if(newWidth > 0) { 58559 this.datatable.setColumnWidth(this.column, newWidth); 58560 } 58561 } 58562 } 58563 }); 58564} 58565 58566///////////////////////////////////////////////////////////////////////////// 58567// 58568// Deprecated 58569// 58570///////////////////////////////////////////////////////////////////////////// 58571 58572/** 58573 * @property editorOptions 58574 * @deprecated Pass configs directly to CellEditor constructor. 58575 */ 58576 58577 58578(function () { 58579 58580var lang = YAHOO.lang, 58581 util = YAHOO.util, 58582 widget = YAHOO.widget, 58583 58584 Dom = util.Dom, 58585 Ev = util.Event, 58586 DT = widget.DataTable; 58587 58588/****************************************************************************/ 58589/****************************************************************************/ 58590/****************************************************************************/ 58591 58592/** 58593 * A RecordSet defines and manages a set of Records. 58594 * 58595 * @namespace YAHOO.widget 58596 * @class RecordSet 58597 * @param data {Object || Object[]} An object literal or an array of data. 58598 * @constructor 58599 */ 58600YAHOO.widget.RecordSet = function(data) { 58601 this._init(data); 58602}; 58603 58604var RS = widget.RecordSet; 58605 58606/**
58607 * Internal class variable to name multiple Recordset instances. 58608 * 58609 * @property RecordSet._nCount 58610 * @type Number 58611 * @private 58612 * @static 58613 */ 58614RS._nCount = 0; 58615 58616RS.prototype = { 58617 58618 ///////////////////////////////////////////////////////////////////////////// 58619 // 58620 // Private member variables 58621 // 58622 ///////////////////////////////////////////////////////////////////////////// 58623 /** 58624 * Unique String identifier assigned at instantiation. 58625 * 58626 * @property _sId 58627 * @type String 58628 * @private 58629 */ 58630 _sId : null, 58631 58632 /** 58633 * Internal counter of how many Records are in the RecordSet. 58634 * 58635 * @property _length 58636 * @type Number 58637 * @private 58638 * @deprecated No longer used 58639 */ 58640 //_length : null, 58641 58642 ///////////////////////////////////////////////////////////////////////////// 58643 // 58644 // Private methods 58645 // 58646 ///////////////////////////////////////////////////////////////////////////// 58647 58648 /** 58649 * Initializer. 58650 * 58651 * @method _init 58652 * @param data {Object || Object[]} An object literal or an array of data. 58653 * @private 58654 */ 58655 _init : function(data) { 58656 // Internal variables 58657 this._sId = Dom.generateId(null, "yui-rs");// "yui-rs" + widget.RecordSet._nCount; 58658 widget.RecordSet._nCount++; 58659 this._records = []; 58660 //this._length = 0; 58661 58662 this._initEvents(); 58663 58664 if(data) { 58665 if(lang.isArray(data)) { 58666 this.addRecords(data); 58667 } 58668 else if(lang.isObject(data)) { 58669 this.addRecord(data); 58670 } 58671 } 58672 58673 YAHOO.log("RecordSet initialized", "info", this.toString()); 58674 }, 58675 58676 /** 58677 * Initializes custom events. 58678 * 58679 * @method _initEvents 58680 * @private 58681 */ 58682 _initEvents : function() { 58683 this.createEvent("recordAddEvent"); 58684 this.createEvent("recordsAddEvent"); 58685 this.createEvent("recordSetEvent"); 58686 this.createEvent("recordsSetEvent"); 58687 this.createEvent("recordUpdateEvent"); 58688 this.createEvent("recordDeleteEvent"); 58689 this.createEvent("recordsDeleteEvent"); 58690 this.createEvent("resetEvent"); 58691 this.createEvent("recordValueUpdateEvent"); 58692 }, 58693 58694 /** 58695 * Adds one Record to the RecordSet at the given index. If index is null, 58696 * then adds the Record to the end of the RecordSet. 58697 * 58698 * @method _addRecord 58699 * @param oData {Object} An object literal of data. 58700 * @param index {Number} (optional) Position index. 58701 * @return {YAHOO.widget.Record} A Record instance. 58702 * @private 58703 */
58704 _addRecord : function(oData, index) { 58705 var oRecord = new YAHOO.widget.Record(oData); 58706 58707 if(YAHOO.lang.isNumber(index) && (index > -1)) { 58708 this._records.splice(index,0,oRecord); 58709 } 58710 else { 58711 //index = this.getLength(); 58712 //this._records[index] = oRecord; 58713 this._records[this._records.length] = oRecord; 58714 } 58715 //this._length++; 58716 return oRecord; 58717 }, 58718 58719 /** 58720 * Sets/replaces one Record to the RecordSet at the given index. Existing 58721 * Records with higher indexes are not shifted. If no index specified, the 58722 * Record is added to the end of the RecordSet. 58723 * 58724 * @method _setRecord 58725 * @param oData {Object} An object literal of data. 58726 * @param index {Number} (optional) Position index. 58727 * @return {YAHOO.widget.Record} A Record instance. 58728 * @private 58729 */ 58730 _setRecord : function(oData, index) { 58731 if (!lang.isNumber(index) || index < 0) { 58732 index = this._records.length; 58733 } 58734 return (this._records[index] = new widget.Record(oData)); 58735 /* 58736 if(lang.isNumber(index) && (index > -1)) { 58737 this._records[index] = oRecord; 58738 if((index+1) > this.getLength()) { 58739 this._length = index+1; 58740 } 58741 } 58742 else { 58743 this._records[this.getLength()] = oRecord; 58744 this._length++; 58745 } 58746 return oRecord; 58747 */ 58748 }, 58749 58750 /** 58751 * Deletes Records from the RecordSet at the given index. If range is null, 58752 * then only one Record is deleted. 58753 * 58754 * @method _deleteRecord 58755 * @param index {Number} Position index. 58756 * @param range {Number} (optional) How many Records to delete 58757 * @private 58758 */ 58759 _deleteRecord : function(index, range) { 58760 if(!lang.isNumber(range) || (range < 0)) { 58761 range = 1; 58762 } 58763 this._records.splice(index, range); 58764 //this._length = this._length - range; 58765 }, 58766 58767 ///////////////////////////////////////////////////////////////////////////// 58768 // 58769 // Public methods 58770 // 58771 ///////////////////////////////////////////////////////////////////////////// 58772 58773 /** 58774 * Returns unique name of the RecordSet instance. 58775 * 58776 * @method getId 58777 * @return {String} Unique name of the RecordSet instance. 58778 */ 58779 getId : function() { 58780 return this._sId; 58781 }, 58782 58783 /** 58784 * Public accessor to the unique name of the RecordSet instance. 58785 * 58786 * @method toString 58787 * @return {String} Unique name of the RecordSet instance. 58788 */ 58789 toString : function() { 58790 return "RecordSet instance " + this._sId; 58791 }, 58792 58793 /** 58794 * Returns the number of Records held in the RecordSet. 58795 * 58796 * @method getLength 58797 * @return {Number} Number of records in the RecordSet. 58798 */ 58799 getLength : function() { 58800 //return this._length; 58801 return this._records.length; 58802 }, 58803 58804 /** 58805 * Returns Record by ID or RecordSet position index. 58806 * 58807 * @method getRecord 58808 * @param record {YAHOO.widget.Record | Number | String} Record instance, 58809 * RecordSet position index, or Record ID. 58810 * @return {YAHOO.widget.Record} Record object. 58811 */ 58812 getRecord : function(record) { 58813 var i; 58814 if(record instanceof widget.Record) { 58815 for(i=0; i<this._records.length; i++) { 58816 if(this._records[i] && (this._records[i]._sId === record._sId)) { 58817 return record; 58818 } 58819 } 58820 } 58821 else if(lang.isNumber(record)) { 58822 if((record > -1) && (record < this.getLength())) { 58823 return this._records[record]; 58824 } 58825 } 58826 else if(lang.isString(record)) { 58827 for(i=0; i<this._records.length; i++) { 58828 if(this._records[i] && (this._records[i]._sId === record)) { 58829 return this._records[i]; 58830 } 58831 } 58832 } 58833 // Not a valid Record for this RecordSet 58834 return null; 58835 58836 }, 58837 58838 /** 58839 * Returns an array of Records from the RecordSet. 58840 * 58841 * @method getRecords 58842 * @param index {Number} (optional) Recordset position index of which Record to 58843 * start at. 58844 * @param range {Number} (optional) Number of Records to get. 58845 * @return {YAHOO.widget.Record[]} Array of Records starting at given index and 58846 * length equal to given range. If index is not given, all Records are returned. 58847 */ 58848 getRecords : function(index, range) { 58849 if(!lang.isNumber(index)) { 58850 return this._records; 58851 } 58852 if(!lang.isNumber(range)) { 58853 return this._records.slice(index); 58854 } 58855 return this._records.slice(index, index+range); 58856 }, 58857 58858 /** 58859 * Returns a boolean indicating whether Records exist in the RecordSet at the 58860 * specified index range. Returns true if and only if a Record exists at each 58861 * index in the range. 58862 * @method hasRecords 58863 * @param index 58864 * @param range 58865 * @return {Boolean} true if all indices are populated in the RecordSet 58866 */ 58867 hasRecords : function (index, range) { 58868 var recs = this.getRecords(index,range); 58869 for (var i = 0; i < range;
58869 ++i) { 58870 if (typeof recs[i] === 'undefined') { 58871 return false; 58872 } 58873 } 58874 return true; 58875 }, 58876 58877 /** 58878 * Returns current position index for the given Record. 58879 * 58880 * @method getRecordIndex 58881 * @param oRecord {YAHOO.widget.Record} Record instance. 58882 * @return {Number} Record's RecordSet position index. 58883 */ 58884 58885 getRecordIndex : function(oRecord) { 58886 if(oRecord) { 58887 for(var i=this._records.length-1; i>-1; i--) { 58888 if(this._records[i] && oRecord.getId() === this._records[i].getId()) { 58889 return i; 58890 } 58891 } 58892 } 58893 return null; 58894 58895 }, 58896 58897 /** 58898 * Adds one Record to the RecordSet at the given index. If index is null, 58899 * then adds the Record to the end of the RecordSet. 58900 * 58901 * @method addRecord 58902 * @param oData {Object} An object literal of data. 58903 * @param index {Number} (optional) Position index. 58904 * @return {YAHOO.widget.Record} A Record instance. 58905 */
58906 addRecord : function(oData, index) { 58907 if(lang.isObject(oData)) { 58908 var oRecord = this._addRecord(oData, index); 58909 this.fireEvent("recordAddEvent",{record:oRecord,data:oData}); 58910 YAHOO.log("Added Record at index " + index + 58911 " with data " + lang.dump(oData), "info", this.toString()); 58912 return oRecord; 58913 } 58914 else { 58915 YAHOO.log("Could not add Record with data" + 58916 lang.dump(oData), "info", this.toString()); 58917 return null; 58918 } 58919 }, 58920 58921 /** 58922 * Adds multiple Records at once to the RecordSet at the given index with the 58923 * given object literal data. If index is null, then the new Records are 58924 * added to the end of the RecordSet. 58925 * 58926 * @method addRecords 58927 * @param aData {Object[]} An object literal data or an array of data object literals. 58928 * @param index {Number} (optional) Position index. 58929 * @return {YAHOO.widget.Record[]} An array of Record instances. 58930 */ 58931 addRecords : function(aData, index) { 58932 if(lang.isArray(aData)) { 58933 var newRecords = [], 58934 idx,i,len; 58935 58936 index = lang.isNumber(index) ? index : this._records.length; 58937 idx = index; 58938 58939 // Can't go backwards bc we need to preserve order 58940 for(i=0,len=aData.length; i<len; ++i) { 58941 if(lang.isObject(aData[i])) { 58942 var record = this._addRecord(aData[i], idx++); 58943 newRecords.push(record); 58944 } 58945 } 58946 this.fireEvent("recordsAddEvent",{records:newRecords,data:aData}); 58947 YAHOO.log("Added " + newRecords.length + " Record(s) at index " + index + 58948 " with data " + lang.dump(aData), "info", this.toString()); 58949 return newRecords; 58950 } 58951 else if(lang.isObject(aData)) { 58952 var oRecord = this._addRecord(aData); 58953 this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData}); 58954 YAHOO.log("Added 1 Record at index " + index + 58955 " with data " + lang.dump(aData), "info", this.toString()); 58956 return oRecord; 58957 } 58958 else { 58959 YAHOO.log("Could not add Records with data " + 58960 lang.dump(aData), "info", this.toString()); 58961 return null; 58962 } 58963 }, 58964 58965 /** 58966 * Sets or replaces one Record to the RecordSet at the given index. Unlike 58967 * addRecord, an existing Record at that index is not shifted to preserve it. 58968 * If no index is specified, it adds the Record to the end of the RecordSet. 58969 * 58970 * @method setRecord 58971 * @param oData {Object} An object literal of data. 58972 * @param index {Number} (optional) Position index. 58973 * @return {YAHOO.widget.Record} A Record instance. 58974 */
58975 setRecord : function(oData, index) { 58976 if(lang.isObject(oData)) { 58977 var oRecord = this._setRecord(oData, index); 58978 this.fireEvent("recordSetEvent",{record:oRecord,data:oData}); 58979 YAHOO.log("Set Record at index " + index + 58980 " with data " + lang.dump(oData), "info", this.toString()); 58981 return oRecord; 58982 } 58983 else { 58984 YAHOO.log("Could not set Record with data" + 58985 lang.dump(oData), "info", this.toString()); 58986 return null; 58987 } 58988 }, 58989 58990 /** 58991 * Sets or replaces multiple Records at once to the RecordSet with the given 58992 * data, starting at the given index. If index is not specified, then the new 58993 * Records are added to the end of the RecordSet. 58994 * 58995 * @method setRecords 58996 * @param aData {Object[]} An array of object literal data. 58997 * @param index {Number} (optional) Position index. 58998 * @return {YAHOO.widget.Record[]} An array of Record instances. 58999 */ 59000 setRecords : function(aData, index) { 59001 var Rec = widget.Record, 59002 a = lang.isArray(aData) ? aData : [aData], 59003 added = [], 59004 i = 0, l = a.length, j = 0; 59005 59006 index = parseInt(index,10)|0; 59007 59008 for(; i < l; ++i) { 59009 if (typeof a[i] === 'object' && a[i]) { 59010 added[j++] = this._records[index + i] = new Rec(a[i]); 59011 } 59012 } 59013 59014 this.fireEvent("recordsSetEvent",{records:added,data:aData}); 59015 // Backward compatibility for bug 1918245 59016 this.fireEvent("recordsSet",{records:added,data:aData}); 59017 YAHOO.log("Set "+j+" Record(s) at index "+index, "info", 59018 this.toString()); 59019 59020 if (a.length && !added.length) { 59021 YAHOO.log("Could not set Records with data " + 59022 lang.dump(aData), "info", this.toString()); 59023 } 59024 59025 return added; 59026 }, 59027 59028 /** 59029 * Updates given Record with given data. 59030 * 59031 * @method updateRecord 59032 * @param record {YAHOO.widget.Record | Number | String} A Record instance, 59033 * a RecordSet position index, or a Record ID. 59034 * @param oData {Object} Object literal of new data. 59035 * @return {YAHOO.widget.Record} Updated Record, or null. 59036 */ 59037 updateRecord : function(record, oData) { 59038 var oRecord = this.getRecord(record); 59039 if(oRecord && lang.isObject(oData)) { 59040 // Copy data from the Record for the event that gets fired later 59041 var oldData = {}; 59042 for(var key in oRecord._oData) { 59043 if(lang.hasOwnProperty(oRecord._oData, key)) { 59044 oldData[key] = oRecord._oData[key]; 59045 } 59046 } 59047 oRecord._oData = oData; 59048 this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData}); 59049 YAHOO.log("Record at index " + this.getRecordIndex(oRecord) + 59050 " updated with data " + lang.dump(oData), "info", this.toString()); 59051 return oRecord; 59052 } 59053 else { 59054 YAHOO.log("Could not update Record " + record, "error", this.toString()); 59055 return null; 59056 } 59057 }, 59058 59059 /** 59060 * @method updateKey 59061 * @deprecated Use updateRecordValue 59062 */ 59063 updateKey : function(record, sKey, oData) { 59064 this.updateRecordValue(record, sKey, oData); 59065 }, 59066 /** 59067 * Sets given Record at given key to given data. 59068 * 59069 * @method updateRecordValue 59070 * @param record {YAHOO.widget.Record | Number | String} A Record instance, 59071 * a RecordSet position index, or a Record ID. 59072 * @param sKey {String} Key name. 59073 * @param oData {Object} New data. 59074 */ 59075 updateRecordValue : function(record, sKey, oData) { 59076 var oRecord = this.getRecord(record); 59077 if(oRecord) { 59078 var oldData = null; 59079 var keyValue = oRecord._oData[sKey]; 59080 // Copy data from the Record for the event that gets fired later 59081 if(keyValue && lang.isObject(keyValue)) { 59082 oldData = {}; 59083 for(var key in keyValue) { 59084 if(lang.hasOwnProperty(keyValue, key)) { 59085 oldData[key] = keyValue[key]; 59086 } 59087 } 59088 } 59089 // Copy by value 59090 else { 59091 oldData = keyValue; 59092 } 59093 59094 oRecord._oData[sKey] = oData; 59095 this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData}
59095); 59096 this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData}); 59097 YAHOO.log("Key \"" + sKey + 59098 "\" for Record at index " + this.getRecordIndex(oRecord) + 59099 " updated to \"" + lang.dump(oData) + "\"", "info", this.toString()); 59100 } 59101 else { 59102 YAHOO.log("Could not update key " + sKey + " for Record " + record, "error", this.toString()); 59103 } 59104 }, 59105 59106 /** 59107 * Replaces all Records in RecordSet with new object literal data. 59108 * 59109 * @method replaceRecords 59110 * @param data {Object || Object[]} An object literal of data or an array of 59111 * data object literals. 59112 * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or 59113 * an array of Records. 59114 */ 59115 replaceRecords : function(data) { 59116 this.reset(); 59117 return this.addRecords(data); 59118 }, 59119 59120 /** 59121 * Sorts all Records by given function. Records keep their unique IDs but will 59122 * have new RecordSet position indexes. 59123 * 59124 * @method sortRecords 59125 * @param fnSort {Function} Reference to a sort function. 59126 * @param desc {Boolean} True if sort direction is descending, false if sort 59127 * direction is ascending. 59128 * @param field {String} The field to sort by, from sortOptions.field 59129 * @return {YAHOO.widget.Record[]} Sorted array of Records. 59130 */ 59131 sortRecords : function(fnSort, desc, field) { 59132 return this._records.sort(function(a, b) {return fnSort(a, b, desc, field);}); 59133 }, 59134 59135 /** 59136 * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"]. 59137 * 59138 * @method reverseRecords 59139 * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records. 59140 */ 59141 reverseRecords : function() { 59142 return this._records.reverse(); 59143 }, 59144 59145 /** 59146 * Removes the Record at the given position index from the RecordSet. If a range 59147 * is also provided, removes that many Records, starting from the index. Length 59148 * of RecordSet is correspondingly shortened. 59149 * 59150 * @method deleteRecord 59151 * @param index {Number} Record's RecordSet position index. 59152 * @return {Object} A copy of the data held by the deleted Record. 59153 */ 59154 deleteRecord : function(index) { 59155 if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) { 59156 var oData = this.getRecord(index).getData(); 59157 59158 this._deleteRecord(index); 59159 this.fireEvent("recordDeleteEvent",{data:oData,index:index}); 59160 YAHOO.log("Record deleted at index " + index + 59161 " and containing data " + lang.dump(oData), "info", this.toString()); 59162 return oData; 59163 } 59164 else { 59165 YAHOO.log("Could not delete Record at index " + index, "error", this.toString()); 59166 return null; 59167 } 59168 }, 59169 59170 /** 59171 * Removes the Record at the given position index from the RecordSet. If a range 59172 * is also provided, removes that many Records, starting from the index. Length 59173 * of RecordSet is correspondingly shortened. 59174 * 59175 * @method deleteRecords 59176 * @param index {Number} Record's RecordSet position index. 59177 * @param range {Number} (optional) How many Records to delete. 59178 * @return {Object[]} An array of copies of the data held by the deleted Records. 59179 */ 59180 deleteRecords : function(index, range) { 59181 if(!lang.isNumber(range)) { 59182 range = 1; 59183 } 59184 if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) { 59185 var recordsToDelete = this.getRecords(index, range); 59186 var deletedData = [], // this mistakenly held Records, not data 59187 deletedObjects = []; // this passes data only 59188 59189 for(var i=0; i<recordsToDelete.length; i++) { 59190 deletedData[deletedData.length] = recordsToDelete[i]; // backward compatibility 59191 deletedObjects[deletedObjects.length] = recordsToDelete[i].getData(); 59192 } 59193 this._deleteRecord(index, range); 59194 59195 this.fireEvent("recordsDeleteEvent",{data:deletedData,deletedData:deletedObjects,index:index}); 59196 YAHOO.log(range + "Record(s) deleted at index " + index + 59197 " and containing data " + lang.dump(deletedObjects), "info", this.toString()); 59198 59199 return deletedData; 59200 } 59201 else { 59202 YAHOO.log("Could not delete Records at index " + index, "error", this.toString()); 59203 return null; 59204 } 59205 }, 59206 59207 /** 59208 * Deletes all Records from the RecordSet. 59209 * 59210 * @method reset 59211 */ 59212 reset : function() { 59213 this._records = []; 59214 //this._length = 0; 59215 this.fireEvent("resetEvent"); 59216 YAHOO.log("All Records deleted from RecordSet", "info", this.toString()); 59217 } 59218}; 59219 59220///////////////////////////////////////////////////////////////////////////// 59221// 59222// Custom Events 59223// 59224///////////////////////////////////////////////////////////////////////////// 59225 59226// RecordSet uses EventProvider 59227lang.augmentProto(RS, util.EventProvider); 59228 59229/** 59230 * Fired when a new Record is added to the RecordSet. 59231 * 59232 * @event recordAddEvent 59233 * @param oArgs.record {YAHOO.widget.Record} The Record instance. 59234 * @param oArgs.data {Object} Data added. 59235 */ 59236 59237/** 59238 * Fired when multiple Records are added to the RecordSet at once. 59239 * 59240 * @event recordsAddEvent 59241 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances. 59242 * @param oArgs.data {Object[]} Data added. 59243 */ 59244 59245/** 59246 * Fired when a Record is set in the RecordSet. 59247 * 59248 * @event recordSetEvent 59249 * @param oArgs.record {YAHOO.widget.Record} The Record instance. 59250 * @param oArgs.data {Object} Data added. 59251 */ 59252 59253/** 59254 * Fired when multiple Records are set in the RecordSet at once. 59255 * 59256 * @event recordsSetEvent 59257 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances. 59258 * @param oArgs.data {Object[]} Data added. 59259 */ 59260 59261/** 59262 * Fired when a Record is updated with new data. 59263 * 59264 * @event recordUpdateEvent 59265 * @param oArgs.record {YAHOO.widget.Record} The Record instance. 59266 * @param oArgs.newData {Object} New data. 59267 * @param oArgs.oldData {Object} Old data. 59268 */ 59269 59270/** 59271 * Fired when a Record is deleted from the RecordSet. 59272 * 59273 * @event recordDeleteEvent 59274 * @param oArgs.data {Object} The data held by the deleted Record, 59275 * or an array of data object literals if multiple Records were deleted at once. 59276 * @param oArgs.index {Object} Index of the deleted Record. 59277 */ 59278 59279/** 59280 * Fired when multiple Records are deleted from the RecordSet at once. 59281 * 59282 * @event recordsDeleteEvent 59283 * @param oArgs.data {Object[]} An array of deleted Records. 59284 * @param oArgs.deletedData {Object[]} An array of deleted data. 59285 * @param oArgs.index {Object} Index of the first deleted Record. 59286 */ 59287 59288/** 59289 * Fired when all Records are deleted from the RecordSet at once. 59290 * 59291 * @event resetEvent 59292 */ 59293 59294/** 59295 * @event keyUpdateEvent 59296 * @deprecated Use recordValueUpdateEvent 59297 */ 59298 59299/** 59300 * Fired when a Record value is updated with new data. 59301 * 59302 * @event recordValueUpdateEvent 59303 * @param oArgs.record {YAHOO.widget.Record} The Record instance. 59304 * @param oArgs.key {String} The updated key. 59305 * @param oArgs.newData {Object} New data. 59306 * @param oArgs.oldData {Object} Old data. 59307 * 59308 */ 59309 59310 59311/****************************************************************************/ 59312/****************************************************************************/ 59313/****************************************************************************/ 59314 59315/** 59316 * The Record class defines a DataTable record. 59317 * 59318 * @namespace YAHOO.widget 59319 * @class Record 59320 * @constructor 59321 * @param oConfigs {Object} (optional) Object literal of key/value pairs. 59322 */ 59323YAHOO.widget.Record = function(oLiteral) { 59324 this._nCount = widget.Record._nCount; 59325 this._sId = Dom.generateId(null, "yui-rec");//"yui-rec" + this._nCount; 59326 widget.Record._nCount++; 59327 this._oData = {}; 59328 if(lang.isObject(oLiteral)) { 59329 for(var sKey in oLiteral) { 59330 if(lang.hasOwnProperty(oLiteral, sKey)) { 59331 this._oData[sKey] = oLiteral[sKey]; 59332 } 59333 } 59334 } 59335}; 59336 59337///////////////////////////////////////////////////////////////////////////// 59338// 59339// Private member variables 59340// 59341///////////////////////////////////////////////////////////////////////////// 59342 59343/**
59344 * Internal class variable to give unique IDs to Record instances. 59345 * 59346 * @property Record._nCount 59347 * @type Number 59348 * @private 59349 */ 59350YAHOO.widget.Record._nCount = 0; 59351 59352YAHOO.widget.Record.prototype = { 59353 /** 59354 * Immutable unique count assigned at instantiation. Remains constant while a 59355 * Record's position index can change from sorting. 59356 * 59357 * @property _nCount 59358 * @type Number 59359 * @private 59360 */ 59361 _nCount : null, 59362 59363 /** 59364 * Immutable unique ID assigned at instantiation. Remains constant while a 59365 * Record's position index can change from sorting. 59366 * 59367 * @property _sId 59368 * @type String 59369 * @private 59370 */ 59371 _sId : null, 59372 59373 /** 59374 * Holds data for the Record in an object literal. 59375 * 59376 * @property _oData 59377 * @type Object 59378 * @private 59379 */ 59380 _oData : null, 59381 59382 ///////////////////////////////////////////////////////////////////////////// 59383 // 59384 // Public member variables 59385 // 59386 ///////////////////////////////////////////////////////////////////////////// 59387 59388 ///////////////////////////////////////////////////////////////////////////// 59389 // 59390 // Public methods 59391 // 59392 ///////////////////////////////////////////////////////////////////////////// 59393 59394 /** 59395 * Returns unique count assigned at instantiation. 59396 * 59397 * @method getCount 59398 * @return Number 59399 */ 59400 getCount : function() { 59401 return this._nCount; 59402 }, 59403 59404 /** 59405 * Returns unique ID assigned at instantiation. 59406 * 59407 * @method getId 59408 * @return String 59409 */ 59410 getId : function() { 59411 return this._sId; 59412 }, 59413 59414 /** 59415 * Returns data for the Record for a field if given, or the entire object 59416 * literal otherwise. 59417 * 59418 * @method getData 59419 * @param sField {String} (Optional) The field from which to retrieve data value. 59420 * @return Object 59421 */ 59422 getData : function(sField) { 59423 if(lang.isString(sField)) { 59424 return this._oData[sField]; 59425 } 59426 else { 59427 return this._oData; 59428 } 59429 }, 59430 59431 /** 59432 * Sets given data at the given key. Use the RecordSet method updateRecordValue to trigger 59433 * events. 59434 * 59435 * @method setData 59436 * @param sKey {String} The key of the new value. 59437 * @param oData {MIXED} The new value. 59438 */ 59439 setData : function(sKey, oData) { 59440 this._oData[sKey] = oData; 59441 } 59442}; 59443 59444})(); 59445 59446(function () { 59447 59448var lang = YAHOO.lang, 59449 util = YAHOO.util, 59450 widget = YAHOO.widget, 59451 ua = YAHOO.env.ua, 59452 59453 Dom = util.Dom, 59454 Ev = util.Event, 59455 DS = util.DataSourceBase; 59456 59457/** 59458 * The DataTable widget provides a progressively enhanced DHTML control for 59459 * displaying tabular data across A-grade browsers. 59460 * 59461 * @module datatable 59462 * @requires yahoo, dom, event, element, datasource 59463 * @optional dragdrop, dragdrop 59464 * @title DataTable Widget 59465 */ 59466 59467/****************************************************************************/ 59468/****************************************************************************/ 59469/****************************************************************************/ 59470 59471/** 59472 * DataTable class for the YUI DataTable widget. 59473 * 59474 * @namespace YAHOO.widget 59475 * @class DataTable 59476 * @extends YAHOO.util.Element 59477 * @constructor 59478 * @param elContainer {HTMLElement} Container element for the TABLE. 59479 * @param aColumnDefs {Object[]} Array of object literal Column definitions. 59480 * @param oDataSource {YAHOO.util.DataSource} DataSource instance. 59481 * @param oConfigs {object} (optional) Object literal of configuration values. 59482 */ 59483YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) { 59484 var DT = widget.DataTable; 59485 59486 //////////////////////////////////////////////////////////////////////////// 59487 // Backward compatibility for SDT, but prevent infinite loops 59488 59489 if(oConfigs && oConfigs.scrollable) { 59490 return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs); 59491 } 59492 59493 //////////////////////////////////////////////////////////////////////////// 59494 // Initialization 59495 59496 // Internal vars 59497 this._nIndex = DT._nCount; 59498 this._sId = Dom.generateId(null, "yui-dt");// "yui-dt"+this._nIndex; 59499 this._oChainRender = new YAHOO.util.Chain(); 59500 this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true); 59501 59502 // Initialize configs 59503 this._initConfigs(oConfigs); 59504 59505 // Initialize DataSource 59506 this._initDataSource(oDataSource); 59507 if(!this._oDataSource) { 59508 YAHOO.log("Could not instantiate DataTable due to an invali
59508d DataSource", "error", this.toString()); 59509 return; 59510 } 59511 59512 // Initialize ColumnSet 59513 this._initColumnSet(aColumnDefs); 59514 if(!this._oColumnSet) { 59515 YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString()); 59516 return; 59517 } 59518 59519 // Initialize RecordSet 59520 this._initRecordSet(); 59521 if(!this._oRecordSet) { 59522 } 59523 59524 // Initialize Attributes 59525 DT.superclass.constructor.call(this, elContainer, this.configs); 59526 59527 // Initialize DOM elements 59528 var okDom = this._initDomElements(elContainer); 59529 if(!okDom) { 59530 YAHOO.log("Could not instantiate DataTable due to an invalid DOM element", "error", this.toString()); 59531 return; 59532 } 59533 59534 // Show message as soon as config is available 59535 this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING); 59536 59537 //////////////////////////////////////////////////////////////////////////// 59538 // Once per instance 59539 this._initEvents(); 59540 59541 DT._nCount++; 59542 DT._nCurrentCount++; 59543 59544 //////////////////////////////////////////////////////////////////////////// 59545 // Data integration 59546 59547 // Send a simple initial request 59548 var oCallback = { 59549 success : this.onDataReturnSetRows, 59550 failure : this.onDataReturnSetRows, 59551 scope : this, 59552 argument: this.getState() 59553 }; 59554 59555 var initialLoad = this.get("initialLoad"); 59556 if(initialLoad === true) { 59557 this._oDataSource.sendRequest(this.get("initialRequest"), oCallback); 59558 } 59559 // Do not send an initial request at all 59560 else if(initialLoad === false) { 59561 this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY); 59562 } 59563 // Send an initial request with a custom payload 59564 else { 59565 var oCustom = initialLoad || {}; 59566 oCallback.argument = oCustom.argument || {}; 59567 this._oDataSource.sendRequest(oCustom.request, oCallback); 59568 } 59569}; 59570 59571var DT = widget.DataTable; 59572 59573///////////////////////////////////////////////////////////////////////////// 59574// 59575// Public constants 59576// 59577///////////////////////////////////////////////////////////////////////////// 59578 59579lang.augmentObject(DT, { 59580 59581 /** 59582 * Class name assigned to outer DataTable container. 59583 * 59584 * @property DataTable.CLASS_DATATABLE
59585 * @type String 59586 * @static 59587 * @final 59588 * @default "yui-dt" 59589 */ 59590 CLASS_DATATABLE : "yui-dt", 59591 59592 /** 59593 * Class name assigned to liner DIV elements. 59594 * 59595 * @property DataTable.CLASS_LINER 59596 * @type String 59597 * @static 59598 * @final 59599 * @default "yui-dt-liner" 59600 */ 59601 CLASS_LINER : "yui-dt-liner", 59602 59603 /** 59604 * Class name assigned to display label elements. 59605 * 59606 * @property DataTable.CLASS_LABEL 59607 * @type String 59608 * @static 59609 * @final 59610 * @default "yui-dt-label" 59611 */ 59612 CLASS_LABEL : "yui-dt-label", 59613 59614 /** 59615 * Class name assigned to messaging elements. 59616 * 59617 * @property DataTable.CLASS_MESSAGE 59618 * @type String 59619 * @static 59620 * @final 59621 * @default "yui-dt-message" 59622 */ 59623 CLASS_MESSAGE : "yui-dt-message", 59624 59625 /** 59626 * Class name assigned to mask element when DataTable is disabled. 59627 * 59628 * @property DataTable.CLASS_MASK 59629 * @type String 59630 * @static 59631 * @final 59632 * @default "yui-dt-mask" 59633 */ 59634 CLASS_MASK : "yui-dt-mask", 59635 59636 /** 59637 * Class name assigned to data elements. 59638 * 59639 * @property DataTable.CLASS_DATA
59640 * @type String 59641 * @static 59642 * @final 59643 * @default "yui-dt-data" 59644 */ 59645 CLASS_DATA : "yui-dt-data", 59646 59647 /** 59648 * Class name assigned to Column drag target. 59649 * 59650 * @property DataTable.CLASS_COLTARGET 59651 * @type String 59652 * @static 59653 * @final 59654 * @default "yui-dt-coltarget" 59655 */ 59656 CLASS_COLTARGET : "yui-dt-coltarget", 59657 59658 /** 59659 * Class name assigned to resizer handle elements. 59660 * 59661 * @property DataTable.CLASS_RESIZER 59662 * @type String 59663 * @static 59664 * @final 59665 * @default "yui-dt-resizer" 59666 */ 59667 CLASS_RESIZER : "yui-dt-resizer", 59668 59669 /** 59670 * Class name assigned to resizer liner elements. 59671 * 59672 * @property DataTable.CLASS_RESIZERLINER 59673 * @type String 59674 * @static 59675 * @final 59676 * @default "yui-dt-resizerliner" 59677 */ 59678 CLASS_RESIZERLINER : "yui-dt-resizerliner", 59679 59680 /** 59681 * Class name assigned to resizer proxy elements. 59682 * 59683 * @property DataTable.CLASS_RESIZERPROXY 59684 * @type String 59685 * @static 59686 * @final 59687 * @default "yui-dt-resizerproxy" 59688 */ 59689 CLASS_RESIZERPROXY : "yui-dt-resizerproxy", 59690 59691 /** 59692 * Class name assigned to CellEditor container elements. 59693 * 59694 * @property DataTable.CLASS_EDITOR
59695 * @type String 59696 * @static 59697 * @final 59698 * @default "yui-dt-editor" 59699 */ 59700 CLASS_EDITOR : "yui-dt-editor", 59701 59702 /** 59703 * Class name assigned to CellEditor container shim. 59704 * 59705 * @property DataTable.CLASS_EDITOR_SHIM 59706 * @type String 59707 * @static 59708 * @final 59709 * @default "yui-dt-editor-shim" 59710 */ 59711 CLASS_EDITOR_SHIM : "yui-dt-editor-shim", 59712 59713 /** 59714 * Class name assigned to paginator container elements. 59715 * 59716 * @property DataTable.CLASS_PAGINATOR 59717 * @type String 59718 * @static 59719 * @final 59720 * @default "yui-dt-paginator" 59721 */ 59722 CLASS_PAGINATOR : "yui-dt-paginator", 59723 59724 /** 59725 * Class name assigned to page number indicators. 59726 * 59727 * @property DataTable.CLASS_PAGE 59728 * @type String 59729 * @static 59730 * @final 59731 * @default "yui-dt-page" 59732 */ 59733 CLASS_PAGE : "yui-dt-page", 59734 59735 /** 59736 * Class name assigned to default indicators. 59737 * 59738 * @property DataTable.CLASS_DEFAULT 59739 * @type String 59740 * @static 59741 * @final 59742 * @default "yui-dt-default" 59743 */ 59744 CLASS_DEFAULT : "yui-dt-default", 59745 59746 /** 59747 * Class name assigned to previous indicators. 59748 * 59749 * @property DataTable.CLASS_PREVIOUS
59750 * @type String 59751 * @static 59752 * @final 59753 * @default "yui-dt-previous" 59754 */ 59755 CLASS_PREVIOUS : "yui-dt-previous", 59756 59757 /** 59758 * Class name assigned next indicators. 59759 * 59760 * @property DataTable.CLASS_NEXT 59761 * @type String 59762 * @static 59763 * @final 59764 * @default "yui-dt-next" 59765 */ 59766 CLASS_NEXT : "yui-dt-next", 59767 59768 /** 59769 * Class name assigned to first elements. 59770 * 59771 * @property DataTable.CLASS_FIRST 59772 * @type String 59773 * @static 59774 * @final 59775 * @default "yui-dt-first" 59776 */ 59777 CLASS_FIRST : "yui-dt-first", 59778 59779 /** 59780 * Class name assigned to last elements. 59781 * 59782 * @property DataTable.CLASS_LAST 59783 * @type String 59784 * @static 59785 * @final 59786 * @default "yui-dt-last" 59787 */ 59788 CLASS_LAST : "yui-dt-last", 59789 59790 /** 59791 * Class name assigned to Record elements. 59792 * 59793 * @property DataTable.CLASS_REC 59794 * @type String 59795 * @static 59796 * @final 59797 * @default "yui-dt-rec" 59798 */ 59799 CLASS_REC : "yui-dt-rec", 59800 59801 /** 59802 * Class name assigned to even elements. 59803 * 59804 * @property DataTable.CLASS_EVEN
59805 * @type String 59806 * @static 59807 * @final 59808 * @default "yui-dt-even" 59809 */ 59810 CLASS_EVEN : "yui-dt-even", 59811 59812 /** 59813 * Class name assigned to odd elements. 59814 * 59815 * @property DataTable.CLASS_ODD 59816 * @type String 59817 * @static 59818 * @final 59819 * @default "yui-dt-odd" 59820 */ 59821 CLASS_ODD : "yui-dt-odd", 59822 59823 /** 59824 * Class name assigned to selected elements. 59825 * 59826 * @property DataTable.CLASS_SELECTED 59827 * @type String 59828 * @static 59829 * @final 59830 * @default "yui-dt-selected" 59831 */ 59832 CLASS_SELECTED : "yui-dt-selected", 59833 59834 /** 59835 * Class name assigned to highlighted elements. 59836 * 59837 * @property DataTable.CLASS_HIGHLIGHTED 59838 * @type String 59839 * @static 59840 * @final 59841 * @default "yui-dt-highlighted" 59842 */ 59843 CLASS_HIGHLIGHTED : "yui-dt-highlighted", 59844 59845 /** 59846 * Class name assigned to hidden elements. 59847 * 59848 * @property DataTable.CLASS_HIDDEN 59849 * @type String 59850 * @static 59851 * @final 59852 * @default "yui-dt-hidden" 59853 */ 59854 CLASS_HIDDEN : "yui-dt-hidden", 59855 59856 /** 59857 * Class name assigned to disabled elements. 59858 * 59859 * @property DataTable.CLASS_DISABLED
59860 * @type String 59861 * @static 59862 * @final 59863 * @default "yui-dt-disabled" 59864 */ 59865 CLASS_DISABLED : "yui-dt-disabled", 59866 59867 /** 59868 * Class name assigned to empty indicators. 59869 * 59870 * @property DataTable.CLASS_EMPTY 59871 * @type String 59872 * @static 59873 * @final 59874 * @default "yui-dt-empty" 59875 */ 59876 CLASS_EMPTY : "yui-dt-empty", 59877 59878 /** 59879 * Class name assigned to loading indicatorx. 59880 * 59881 * @property DataTable.CLASS_LOADING 59882 * @type String 59883 * @static 59884 * @final 59885 * @default "yui-dt-loading" 59886 */ 59887 CLASS_LOADING : "yui-dt-loading", 59888 59889 /** 59890 * Class name assigned to error indicators. 59891 * 59892 * @property DataTable.CLASS_ERROR 59893 * @type String 59894 * @static 59895 * @final 59896 * @default "yui-dt-error" 59897 */ 59898 CLASS_ERROR : "yui-dt-error", 59899 59900 /** 59901 * Class name assigned to editable elements. 59902 * 59903 * @property DataTable.CLASS_EDITABLE 59904 * @type String 59905 * @static 59906 * @final 59907 * @default "yui-dt-editable" 59908 */ 59909 CLASS_EDITABLE : "yui-dt-editable", 59910 59911 /** 59912 * Class name assigned to draggable elements. 59913 * 59914 * @property DataTable.CLASS_DRAGGABLE
59915 * @type String 59916 * @static 59917 * @final 59918 * @default "yui-dt-draggable" 59919 */ 59920 CLASS_DRAGGABLE : "yui-dt-draggable", 59921 59922 /** 59923 * Class name assigned to resizeable elements. 59924 * 59925 * @property DataTable.CLASS_RESIZEABLE 59926 * @type String 59927 * @static 59928 * @final 59929 * @default "yui-dt-resizeable" 59930 */ 59931 CLASS_RESIZEABLE : "yui-dt-resizeable", 59932 59933 /** 59934 * Class name assigned to scrollable elements. 59935 * 59936 * @property DataTable.CLASS_SCROLLABLE 59937 * @type String 59938 * @static 59939 * @final 59940 * @default "yui-dt-scrollable" 59941 */ 59942 CLASS_SCROLLABLE : "yui-dt-scrollable", 59943 59944 /** 59945 * Class name assigned to sortable elements. 59946 * 59947 * @property DataTable.CLASS_SORTABLE 59948 * @type String 59949 * @static 59950 * @final 59951 * @default "yui-dt-sortable" 59952 */ 59953 CLASS_SORTABLE : "yui-dt-sortable", 59954 59955 /** 59956 * Class name assigned to ascending elements. 59957 * 59958 * @property DataTable.CLASS_ASC 59959 * @type String 59960 * @static 59961 * @final 59962 * @default "yui-dt-asc" 59963 */ 59964 CLASS_ASC : "yui-dt-asc", 59965 59966 /** 59967 * Class name assigned to descending elements. 59968 * 59969 * @property DataTable.CLASS_DESC
59970 * @type String 59971 * @static 59972 * @final 59973 * @default "yui-dt-desc" 59974 */ 59975 CLASS_DESC : "yui-dt-desc", 59976 59977 /** 59978 * Class name assigned to BUTTON elements and/or container elements. 59979 * 59980 * @property DataTable.CLASS_BUTTON 59981 * @type String 59982 * @static 59983 * @final 59984 * @default "yui-dt-button" 59985 */ 59986 CLASS_BUTTON : "yui-dt-button", 59987 59988 /** 59989 * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements. 59990 * 59991 * @property DataTable.CLASS_CHECKBOX 59992 * @type String 59993 * @static 59994 * @final 59995 * @default "yui-dt-checkbox" 59996 */ 59997 CLASS_CHECKBOX : "yui-dt-checkbox", 59998 59999 /** 60000 * Class name assigned to SELECT elements and/or container elements. 60001 * 60002 * @property DataTable.CLASS_DROPDOWN 60003 * @type String 60004 * @static 60005 * @final 60006 * @default "yui-dt-dropdown" 60007 */ 60008 CLASS_DROPDOWN : "yui-dt-dropdown", 60009 60010 /** 60011 * Class name assigned to INPUT TYPE=RADIO elements and/or container elements. 60012 * 60013 * @property DataTable.CLASS_RADIO
60014 * @type String 60015 * @static 60016 * @final 60017 * @default "yui-dt-radio" 60018 */ 60019 CLASS_RADIO : "yui-dt-radio", 60020 60021 ///////////////////////////////////////////////////////////////////////// 60022 // 60023 // Private static properties 60024 // 60025 ///////////////////////////////////////////////////////////////////////// 60026 60027 /** 60028 * Internal class variable for indexing multiple DataTable instances. 60029 * 60030 * @property DataTable._nCount 60031 * @type Number 60032 * @private 60033 * @static 60034 */ 60035 _nCount : 0, 60036 60037 /** 60038 * Internal class variable tracking current number of DataTable instances, 60039 * so that certain class values can be reset when all instances are destroyed. 60040 * 60041 * @property DataTable._nCurrentCount 60042 * @type Number 60043 * @private 60044 * @static 60045 */ 60046 _nCurrentCount : 0, 60047 60048 /** 60049 * Reference to the STYLE node that is dynamically created and updated 60050 * in order to manage Column widths. 60051 * 60052 * @property DataTable._elDynStyleNode 60053 * @type HTMLElement 60054 * @private 60055 * @static 60056 */ 60057 _elDynStyleNode : null, 60058 60059 /** 60060 * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility. 60061 * 60062 * @property DataTable._bDynStylesFallback 60063 * @type boolean 60064 * @private 60065 * @static 60066 */ 60067 _bDynStylesFallback : (ua.ie) ? true : false, 60068 60069 /** 60070 * Object literal hash of Columns and their dynamically create style rules. 60071 * 60072 * @property DataTable._oDynStyles 60073 * @type Object 60074 * @private 60075 * @static 60076 */ 60077 _oDynStyles : {}, 60078 60079 ///////////////////////////////////////////////////////////////////////// 60080 // 60081 // Private static methods 60082 // 60083 ///////////////////////////////////////////////////////////////////////// 60084 60085 /** 60086 * Clones object literal or array of object literals. 60087 * 60088 * @method DataTable._cloneObject 60089 * @param o {Object} Object. 60090 * @private 60091 * @static 60092 */ 60093 _cloneObject: function(o) { 60094 if(!lang.isValue(o)) { 60095 return o; 60096 } 60097 60098 var copy = {}; 60099 60100 if(o instanceof YAHOO.widget.BaseCellEditor) { 60101 copy = o; 60102 } 60103 else if(Object.prototype.toString.apply(o) === "[object RegExp]") { 60104 copy = o; 60105 } 60106 else if(lang.isFunction(o)) { 60107 copy = o; 60108 } 60109 else if(lang.isArray(o)) { 60110 var array = []; 60111 for(var i=0,len=o.length;i<len;i++) { 60112 array[i] = DT._cloneObject(o[i]); 60113 } 60114 copy = array; 60115 } 60116 else if(lang.isObject(o)) { 60117 for (var x in o){ 60118 if(lang.hasOwnProperty(o, x)) { 60119 if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) { 60120 copy[x] = DT._cloneObject(o[x]); 60121 } 60122 else { 60123 copy[x] = o[x]; 60124 } 60125 } 60126 } 60127 } 60128 else { 60129 copy = o; 60130 } 60131 60132 return copy; 60133 }, 60134 60135 /** 60136 * Formats a BUTTON element. 60137 * 60138 * @method DataTable.formatButton 60139 * @param el {HTMLElement} The element to format with markup. 60140 * @param oRecord {YAHOO.widget.Record} Record instance. 60141 * @param oColumn {YAHOO.widget.Column} Column instance. 60142 * @param oData {HTML} Data value for the cell. By default, the value 60143 * is what gets written to the BUTTON. String values are treated as markup 60144 * and inserted into the DOM with innerHTML. 60145 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60146 * @static 60147 */ 60148 formatButton : function(el, oRecord, oColumn, oData, oDataTable) { 60149 var sValue = lang.isValue(oData) ? oData : "Click"; 60150 //TODO: support YAHOO.widget.Button 60151 //if(YAHOO.widget.Button) { 60152 60153 //} 60154 //else { 60155 el.innerHTML = "<button type=\"button\" class=\""+ 60156 DT.CLASS_BUTTON + "\">" + sValue + "</button>"; 60157 //} 60158 }, 60159 60160 /** 60161 * Formats a CHECKBOX element. 60162 * 60163 * @method DataTable.formatCheckbox 60164 * @param el {HTMLElement} The element to format with markup. 60165 * @param oRecord {YAHOO.widget.Record} Record instance. 60166 * @param oColumn {YAHOO.widget.Column} Column instance. 60167 * @param oData {Object | Boolean | HTML} Data value for the cell. Can be a simple 60168 * Boolean to indicate whether checkbox is checked or not. Can be object literal 60169 * {checked:bBoolean, label:sLabel}. String values are treated as markup 60170 * and inserted into the DOM with innerHTML. 60171 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60172 * @static 60173 */ 60174 formatCheckbox : function(el, oRecord, oColumn, oData, oDataTable) { 60175 var bChecked = oData; 60176 bChecked = (bChecked) ? " checked=\"checked\"" : ""; 60177 el.innerHTML = "<input type=\"checkbox\"" + bChecked + 60178 " class=\"" + DT.CLASS_CHECKBOX + "\" />"; 60179 }, 60180 60181 /** 60182 * Formats currency. Default unit is USD. 60183 * 60184 * @method DataTable.formatCurrency 60185 * @param el {HTMLElement} The element to format with markup. 60186 * @param oRecord {YAHOO.widget.Record} Record instance. 60187 * @param oColumn {YAHOO.widget.Column} Column instance. 60188 * @param oData {Number} Data value for the cell. 60189 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60190 * @static 60191 */ 60192 formatCurrency : function(el, oRecord, oColumn, oData, oDataTable) { 60193 var oDT = oDataTable || this; 60194 el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || oDT.get("currencyOptions")); 60195 }, 60196 60197 /** 60198 * Formats JavaScript Dates. 60199 * 60200 * @method DataTable.formatDate 60201 * @param el {HTMLElement} The element to format with markup. 60202 * @param oRecord {YAHOO.widget.Record} Record instance. 60203 * @param oColumn {YAHOO.widget.Column} Column instance. 60204 * @param oData {Object} Data value for the cell, or null. String values are 60205 * treated as markup and inserted into the DOM with innerHTML. 60206 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60207 * @static 60208 */ 60209 formatDate : function(el, oRecord, oColumn, oData, oDataTable) { 60210 var oDT = oDataTable || this, 60211 oConfig = oColumn.dateOptions || oDT.get("dateOptions"); 60212 el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale); 60213 }, 60214 60215 /** 60216 * Formats SELECT elements. 60217 * 60218 * @method DataTable.formatDropdown 60219 * @param el {HTMLElement} The element to format with markup. 60220 * @param oRecord {YAHOO.widget.Record} Record instance. 60221 * @param oColumn {YAHOO.widget.Column} Column instance. 60222 * @param oData {Object} Data value for the cell, or null. String values may 60223 * be treated as markup and inserted into the DOM with innerHTML as element 60224 * label. 60225 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60226 * @static 60227 */ 60228 formatDropdown : function(el, oRecord, oColumn, oData, oDataTable) { 60229 var oDT = oDataTable || this, 60230 selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field), 60231 options = (lang.isArray(oColumn.dropdownOptions)) ? 60232 oColumn.dropdownOptions : null, 60233 60234 selectEl, 60235 collection = el.getElementsByTagName("select"); 60236 60237 // Create the form element only once, so we can attach the onChange listener 60238 if(collection.length === 0) { 60239 // Create SELECT element 60240 selectEl = document.createElement("select"); 60241 selectEl.className = DT.CLASS_DROPDOWN; 60242 selectEl = el.appendChild(selectEl); 60243 60244 // Add event listener 60245 Ev.addListener(selectEl,"change",oDT._onDropdownChange,oDT); 60246 } 60247 60248 selectEl = collection[0]; 60249 60250 // Update the form element 60251 if(selectEl) { 60252 // Clear out previous options 60253 selectEl.innerHTML = ""; 60254 60255 // We have options to populate 60256 if(options) { 60257 // Create OPTION elements 60258 for(var i=0; i<options.length; i++) { 60259 var option = options[i]; 60260 var optionEl = document.createElement("option"); 60261 optionEl.value = (lang.isValue(option.value)) ? 60262 option.value : option;
60263 // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor 60264 optionEl.innerHTML = (lang.isValue(option.text)) ? 60265 option.text : (lang.isValue(option.label)) ? option.label : option; 60266 optionEl = selectEl.appendChild(optionEl); 60267 if (optionEl.value == selectedValue) { 60268 optionEl.selected = true; 60269 } 60270 } 60271 } 60272 // Selected value is our only option 60273 else { 60274 selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>"; 60275 } 60276 } 60277 else { 60278 el.innerHTML = lang.isValue(oData) ? oData : ""; 60279 } 60280 }, 60281 60282 /** 60283 * Formats emails. 60284 * 60285 * @method DataTable.formatEmail 60286 * @param el {HTMLElement} The element to format with markup. 60287 * @param oRecord {YAHOO.widget.Record} Record instance. 60288 * @param oColumn {YAHOO.widget.Column} Column instance. 60289 * @param oData {String} Data value for the cell, or null. Values are 60290 * HTML-escaped. 60291 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60292 * @static 60293 */ 60294 formatEmail : function(el, oRecord, oColumn, oData, oDataTable) { 60295 if(lang.isString(oData)) { 60296 oData = lang.escapeHTML(oData); 60297 el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>"; 60298 } 60299 else { 60300 el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : ""; 60301 } 60302 }, 60303 60304 /** 60305 * Formats links. 60306 * 60307 * @method DataTable.formatLink 60308 * @param el {HTMLElement} The element to format with markup. 60309 * @param oRecord {YAHOO.widget.Record} Record instance. 60310 * @param oColumn {YAHOO.widget.Column} Column instance. 60311 * @param oData {String} Data value for the cell, or null. Values are 60312 * HTML-escaped 60313 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60314 * @static 60315 */ 60316 formatLink : function(el, oRecord, oColumn, oData, oDataTable) { 60317 if(lang.isString(oData)) { 60318 oData = lang.escapeHTML(oData); 60319 el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>"; 60320 } 60321 else { 60322 el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : ""; 60323 } 60324 }, 60325 60326 /** 60327 * Formats numbers. 60328 * 60329 * @method DataTable.formatNumber 60330 * @param el {HTMLElement} The element to format with markup. 60331 * @param oRecord {YAHOO.widget.Record} Record instance. 60332 * @param oColumn {YAHOO.widget.Column} Column instance. 60333 * @param oData {Object} Data value for the cell, or null. 60334 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60335 * @static 60336 */ 60337 formatNumber : function(el, oRecord, oColumn, oData, oDataTable) { 60338 var oDT = oDataTable || this; 60339 el.innerHTML = util.Number.format(oData, oColumn.numberOptions || oDT.get("numberOptions")); 60340 }, 60341 60342 /** 60343 * Formats INPUT TYPE=RADIO elements. 60344 * 60345 * @method DataTable.formatRadio 60346 * @param el {HTMLElement} The element to format with markup. 60347 * @param oRecord {YAHOO.widget.Record} Record instance. 60348 * @param oColumn {YAHOO.widget.Column} Column instance. 60349 * @param oData {Object} (Optional) Data value for the cell. 60350 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60351 * @static 60352 */ 60353 formatRadio : function(el, oRecord, oColumn, oData, oDataTable) { 60354 var oDT = oDataTable || this, 60355 bChecked = oData; 60356 bChecked = (bChecked) ? " checked=\"checked\"" : ""; 60357 el.innerHTML = "<input type=\"radio\"" + bChecked + 60358 " name=\""+oDT.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" + 60359 " class=\"" + DT.CLASS_RADIO+ "\" />"; 60360 }, 60361 60362 /** 60363 * Formats text strings. 60364 * 60365 * @method DataTable.formatText 60366 * @param el {HTMLElement} The element to format with markup. 60367 * @param oRecord {YAHOO.widget.Record} Record instance. 60368 * @param oColumn {YAHOO.widget.Column} Column instance. 60369 * @param oData {String} (Optional) Data value for the cell. Values are 60370 * HTML-escaped. 60371 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60372 * @static 60373 */ 60374 formatText : function(el, oRecord, oColumn, oData, oDataTable) { 60375 var value = (lang.isValue(oData)) ? oData : ""; 60376 el.innerHTML = lang.escapeHTML(value.toString()); 60377 }, 60378 60379 /** 60380 * Formats TEXTAREA elements. 60381 * 60382 * @method DataTable.formatTextarea 60383 * @param el {HTMLElement} The element to format with markup. 60384 * @param oRecord {YAHOO.widget.Record} Record instance. 60385 * @param oColumn {YAHOO.widget.Column} Column instance. 60386 * @param oData {Object} (Optional) Data value for the cell. Values are 60387 * HTML-escaped. 60388 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60389 * @static 60390 */ 60391 formatTextarea : function(el, oRecord, oColumn, oData, oDataTable) { 60392 var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "", 60393 markup = "<textarea>" + value + "</textarea>"; 60394 el.innerHTML = markup; 60395 }, 60396 60397 /** 60398 * Formats INPUT TYPE=TEXT elements. 60399 * 60400 * @method DataTable.formatTextbox 60401 * @param el {HTMLElement} The element to format with markup. 60402 * @param oRecord {YAHOO.widget.Record} Record instance. 60403 * @param oColumn {YAHOO.widget.Column} Column instance. 60404 * @param oData {Object} (Optional) Data value for the cell. Values are 60405 * HTML-escaped. 60406 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60407 * @static 60408 */ 60409 formatTextbox : function(el, oRecord, oColumn, oData, oDataTable) { 60410 var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "", 60411 markup = "<input type=\"text\" value=\"" + value + "\" />"; 60412 el.innerHTML = markup; 60413 }, 60414 60415 /** 60416 * Default cell formatter 60417 * 60418 * @method DataTable.formatDefault 60419 * @param el {HTMLElement} The element to format with markup. 60420 * @param oRecord {YAHOO.widget.Record} Record instance. 60421 * @param oColumn {YAHOO.widget.Column} Column instance. 60422 * @param oData {HTML} (Optional) Data value for the cell. String values are 60423 * treated as markup and inserted into the DOM with innerHTML. 60424 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. 60425 * @static 60426 */ 60427 formatDefault : function(el, oRecord, oColumn, oData, oDataTable) { 60428 el.innerHTML = (lang.isValue(oData) && oData !== "") ? oData.toString() : " "; 60429 }, 60430 60431 /** 60432 * Validates data value to type Number, doing type conversion as 60433 * necessary. A valid Number value is return, else null is returned
60434 * if input value does not validate. 60435 * 60436 * 60437 * @method DataTable.validateNumber 60438 * @param oData {Object} Data to validate. 60439 * @static 60440 */ 60441 validateNumber : function(oData) { 60442 //Convert to number 60443 var number = oData * 1; 60444 60445 // Validate 60446 if(lang.isNumber(number)) { 60447 return number; 60448 } 60449 else { 60450 YAHOO.log("Could not validate data " + lang.dump(oData) + " to type Number", "warn", this.toString()); 60451 return undefined; 60452 } 60453 } 60454}); 60455 60456// Done in separate step so referenced functions are defined. 60457/** 60458 * Registry of cell formatting functions, enables shortcut pointers in Column 60459 * definition formatter value (i.e., {key:"myColumn", formatter:"date"}). 60460 * @property DataTable.Formatter 60461 * @type Object 60462 * @static 60463 */ 60464DT.Formatter = { 60465 button : DT.formatButton, 60466 checkbox : DT.formatCheckbox, 60467 currency : DT.formatCurrency, 60468 "date" : DT.formatDate, 60469 dropdown : DT.formatDropdown, 60470 email : DT.formatEmail, 60471 link : DT.formatLink, 60472 "number" : DT.formatNumber, 60473 radio : DT.formatRadio, 60474 text : DT.formatText, 60475 textarea : DT.formatTextarea, 60476 textbox : DT.formatTextbox, 60477 60478 defaultFormatter : DT.formatDefault 60479}; 60480 60481lang.extend(DT, util.Element, { 60482 60483///////////////////////////////////////////////////////////////////////////// 60484// 60485// Superclass methods 60486// 60487///////////////////////////////////////////////////////////////////////////// 60488 60489/** 60490 * Implementation of Element's abstract method. Sets up config values. 60491 * 60492 * @method initAttributes 60493 * @param oConfigs {Object} (Optional) Object literal definition of configuration values. 60494 * @private 60495 */ 60496 60497initAttributes : function(oConfigs) { 60498 oConfigs = oConfigs || {}; 60499 DT.superclass.initAttributes.call(this, oConfigs); 60500 60501 /** 60502 * @attribute summary
60503 * @description String value for the SUMMARY attribute. 60504 * @type String 60505 * @default "" 60506 */ 60507 this.setAttributeConfig("summary", { 60508 value: "", 60509 validator: lang.isString, 60510 method: function(sSummary) { 60511 if(this._elTable) { 60512 this._elTable.summary = sSummary; 60513 } 60514 } 60515 }); 60516 60517 /** 60518 * @attribute selectionMode 60519 * @description Specifies row or cell selection mode. Accepts the following strings: 60520 * <dl> 60521 * <dt>"standard"</dt> 60522 * <dd>Standard row selection with support for modifier keys to enable 60523 * multiple selections.</dd> 60524 * 60525 * <dt>"single"</dt> 60526 * <dd>Row selection with modifier keys disabled to not allow 60527 * multiple selections.</dd> 60528 * 60529 * <dt>"singlecell"</dt> 60530 * <dd>Cell selection with modifier keys disabled to not allow 60531 * multiple selections.</dd> 60532 * 60533 * <dt>"cellblock"</dt> 60534 * <dd>Cell selection with support for modifier keys to enable multiple 60535 * selections in a block-fashion, like a spreadsheet.</dd> 60536 * 60537 * <dt>"cellrange"</dt> 60538 * <dd>Cell selection with support for modifier keys to enable multiple 60539 * selections in a range-fashion, like a calendar.</dd> 60540 * </dl> 60541 * 60542 * @default "standard"
60543 * @type String 60544 */ 60545 this.setAttributeConfig("selectionMode", { 60546 value: "standard", 60547 validator: lang.isString 60548 }); 60549 60550 /** 60551 * @attribute sortedBy 60552 * @description Object literal provides metadata for initial sort values if 60553 * data will arrive pre-sorted: 60554 * <dl> 60555 * <dt>sortedBy.key</dt> 60556 * <dd>{String} Key of sorted Column</dd> 60557 * <dt>sortedBy.dir</dt> 60558 * <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd> 60559 * </dl> 60560 * @type Object | null 60561 */ 60562 this.setAttributeConfig("sortedBy", { 60563 value: null, 60564 // TODO: accepted array for nested sorts 60565 validator: function(oNewSortedBy) { 60566 if(oNewSortedBy) { 60567 return (lang.isObject(oNewSortedBy) && oNewSortedBy.key); 60568 } 60569 else { 60570 return (oNewSortedBy === null); 60571 } 60572 }, 60573 method: function(oNewSortedBy) { 60574 // Stash the previous value 60575 var oOldSortedBy = this.get("sortedBy"); 60576 60577 // Workaround for bug 1827195 60578 this._configs.sortedBy.value = oNewSortedBy; 60579 60580 // Remove ASC/DESC from TH 60581 var oOldColumn, 60582 nOldColumnKeyIndex, 60583 oNewColumn, 60584 nNewColumnKeyIndex; 60585 60586 if(this._elThead) { 60587 if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) { 60588 oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key); 60589 nOldColumnKeyIndex = oOldColumn.getKeyIndex(); 60590 60591 // Remove previous UI from THEAD 60592 var elOldTh = oOldColumn.getThEl(); 60593 Dom.removeClass(elOldTh, oOldSortedBy.dir); 60594 this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy); 60595 oOldColumn.getThEl().setAttribute("aria-sort","none"); 60596 } 60597 if(oNewSortedBy) { 60598 oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key); 60599 nNewColumnKeyIndex = oNewColumn.getKeyIndex(); 60600 60601 // Update THEAD with new UI 60602 var elNewTh = oNewColumn.getThEl(); 60603 // Backward compatibility 60604 if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") || (oNewSortedBy.dir == "desc"))) { 60605 var newClass = (oNewSortedBy.dir == "desc") ? 60606 DT.CLASS_DESC : 60607 DT.CLASS_ASC; 60608 Dom.addClass(elNewTh, newClass); 60609 } 60610 else { 60611 var sortClass = oNewSortedBy.dir || DT.CLASS_ASC; 60612 Dom.addClass(elNewTh, sortClass); 60613 } 60614 this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy); 60615 if (oNewSortedBy.dir == DT.CLASS_ASC) 60616 { 60617 oNewColumn.getThEl().setAttribute("aria-sort","ascending"); 60618 } 60619 else 60620 { 60621 oNewColumn.getThEl().setAttribute("aria-sort","descending"); 60622 } 60623 } 60624 } 60625 60626 if(this._elTbody) { 60627 // Update TBODY UI 60628 this._elTbody.style.display = "none"; 60629 var allRows = this._elTbody.rows, 60630 allCells; 60631 for(var i=allRows.length-1; i>-1; i--) { 60632 allCells = allRows[i].childNodes; 60633 if(allCells[nOldColumnKeyIndex]) { 60634 Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir); 60635 } 60636 if(allCells[nNewColumnKeyIndex]) { 60637 Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir); 60638 } 60639 } 60640 this._elTbody.style.display = ""; 60641 } 60642 60643 this._clearTrTemplateEl(); 60644 } 60645 }); 60646 60647 /** 60648 * @attribute paginator
60649 * @description An instance of YAHOO.widget.Paginator. 60650 * @default null 60651 * @type {Object|YAHOO.widget.Paginator} 60652 */ 60653 this.setAttributeConfig("paginator", { 60654 value : null, 60655 validator : function (val) { 60656 return val === null || val instanceof widget.Paginator; 60657 }, 60658 method : function () { this._updatePaginator.apply(this,arguments); } 60659 }); 60660 60661 /** 60662 * @attribute caption 60663 * @description Value for the CAPTION element. String values are treated as 60664 * markup and inserted into the DOM with innerHTML. NB: Not supported in 60665 * ScrollingDataTable. 60666 * @type HTML 60667 */ 60668 this.setAttributeConfig("caption", { 60669 value: null, 60670 validator: lang.isString, 60671 method: function(sCaption) { 60672 this._initCaptionEl(sCaption); 60673 } 60674 }); 60675 60676 /** 60677 * @attribute draggableColumns 60678 * @description True if Columns are draggable to reorder, false otherwise. 60679 * The Drag & Drop Utility is required to enable this feature. Only top-level 60680 * and non-nested Columns are draggable. Write once. 60681 * @default false 60682 * @type Boolean 60683 */ 60684 this.setAttributeConfig("draggableColumns", { 60685 value: false, 60686 validator: lang.isBoolean, 60687 method: function(oParam) { 60688 if(this._elThead) { 60689 if(oParam) { 60690 this._initDraggableColumns(); 60691 } 60692 else { 60693 this._destroyDraggableColumns(); 60694 } 60695 } 60696 } 60697 }); 60698 60699 /** 60700 * @attribute renderLoopSize 60701 * @description A value greater than 0 enables DOM rendering of rows to be 60702 * executed from a non-blocking timeout queue and sets how many rows to be 60703 * rendered per timeout. Recommended for very large data sets. 60704 * @type Number 60705 * @default 0 60706 */ 60707 this.setAttributeConfig("renderLoopSize", { 60708 value: 0, 60709 validator: lang.isNumber 60710 }); 60711 60712 /** 60713 * @attribute sortFunction 60714 * @description Default Column sort function, receives the following args: 60715 * <dl> 60716 * <dt>a {Object}</dt> 60717 * <dd>First sort argument.</dd> 60718 * <dt>b {Object}</dt> 60719 * <dd>Second sort argument.</dd> 60720 60721 * <dt>desc {Boolean}</dt> 60722 * <dd>True if sort direction is descending, false if 60723 * sort direction is ascending.</dd> 60724 * <dt>field {String}</dt> 60725 * <dd>The field to sort by, from sortOptions.field</dd> 60726 * </dl> 60727 * @type function 60728 */ 60729 this.setAttributeConfig("sortFunction", { 60730 value: function(a, b, desc, field) { 60731 var compare = YAHOO.util.Sort.compare, 60732 sorted = compare(a.getData(field),b.getData(field), desc); 60733 if(sorted === 0) { 60734 return compare(a.getCount(),b.getCount(), desc); // Bug 1932978 60735 } 60736 else { 60737 return sorted; 60738 } 60739 } 60740 }); 60741 60742 /** 60743 * @attribute formatRow 60744 * @description A function that accepts a TR element and its associated Record 60745 * for custom formatting. The function must return TRUE in order to automatically 60746 * continue formatting of child TD elements, else TD elements will not be 60747 * automatically formatted. 60748 * @type function 60749 * @default null 60750 */ 60751 this.setAttributeConfig("formatRow", { 60752 value: null, 60753 validator: lang.isFunction 60754 }); 60755 60756 /** 60757 * @attribute generateRequest 60758 * @description A function that converts an object literal of desired DataTable 60759 * states into a request value which is then passed to the DataSource's 60760 * sendRequest method in order to retrieve data for those states. This 60761 * function is passed an object literal of state data and a reference to the 60762 * DataTable instance: 60763 * 60764 * <dl> 60765 * <dt>pagination<dt> 60766 * <dd> 60767 * <dt>offsetRecord</dt> 60768 * <dd>{Number} Index of the first Record of the desired page</dd> 60769 * <dt>rowsPerPage</dt> 60770 * <dd>{Number} Number of rows per page</dd> 60771 * </dd> 60772 * <dt>sortedBy</dt>
60773 * <dd> 60774 * <dt>key</dt> 60775 * <dd>{String} Key of sorted Column</dd> 60776 * <dt>dir</dt> 60777 * <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd> 60778 * </dd> 60779 * <dt>self</dt> 60780 * <dd>The DataTable instance</dd> 60781 * </dl> 60782 * 60783 * and by default returns a String of syntax: 60784 * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}" 60785 * @type function 60786 * @default HTMLFunction 60787 */ 60788 this.setAttributeConfig("generateRequest", { 60789 value: function(oState, oSelf) { 60790 // Set defaults 60791 oState = oState || {pagination:null, sortedBy:null}; 60792 var sort = encodeURIComponent((oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey()); 60793 var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc"; 60794 var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0; 60795 var results = (oState.pagination) ? oState.pagination.rowsPerPage : null; 60796 60797 // Build the request 60798 return "sort=" + sort + 60799 "&dir=" + dir + 60800 "&startIndex=" + startIndex + 60801 ((results !== null) ? "&results=" + results : ""); 60802 }, 60803 validator: lang.isFunction 60804 }); 60805 60806 /** 60807 * @attribute initialRequest 60808 * @description Defines the initial request that gets sent to the DataSource 60809 * during initialization. Value is ignored if initialLoad is set to any value 60810 * other than true. 60811 * @type MIXED 60812 * @default null 60813 */ 60814 this.setAttributeConfig("initialRequest", { 60815 value: null 60816 }); 60817 60818 /** 60819 * @attribute initialLoad 60820 * @description Determines whether or not to load data at instantiation. By 60821 * default, will trigger a sendRequest() to the DataSource and pass in the 60822 * request defined by initialRequest. If set to false, data will not load 60823 * at instantiation. Alternatively, implementers who wish to work with a 60824 * custom payload may pass in an object literal with the following values: 60825 * 60826 * <dl> 60827 * <dt>request (MIXED)</dt> 60828 * <dd>Request value.</dd> 60829 * 60830 * <dt>argument (MIXED)</dt> 60831 * <dd>Custom data that will be passed through to the callback function.</dd> 60832 * </dl> 60833 * 60834 * 60835 * @type Boolean | Object 60836 * @default true 60837 */ 60838 this.setAttributeConfig("initialLoad", { 60839 value: true 60840 }); 60841 60842 /** 60843 * @attribute dynamicData 60844 * @description If true, sorting and pagination are relegated to the DataSource 60845 * for handling, using the request returned by the "generateRequest" function. 60846 * Each new DataSource response blows away all previous Records. False by default, so 60847 * sorting and pagination will be handled directly on the client side, without 60848 * causing any new requests for data from the DataSource. 60849 * @type Boolean 60850 * @default false 60851 */ 60852 this.setAttributeConfig("dynamicData", { 60853 value: false, 60854 validator: lang.isBoolean 60855 }); 60856 60857 /** 60858 * @attribute MSG_EMPTY 60859 * @description Message to display if DataTable has no data. String 60860 * values are treated as markup and inserted into the DOM with innerHTML. 60861 * @type HTML 60862 * @default "No records found." 60863 */ 60864 this.setAttributeConfig("MSG_EMPTY", { 60865 value: "No records found.", 60866 validator: lang.isString 60867 }); 60868 60869 /** 60870 * @attribute MSG_LOADING 60871 * @description Message to display while DataTable is loading data. String 60872 * values are treated as markup and inserted into the DOM with innerHTML. 60873 * @type HTML 60874 * @default "Loading..." 60875 */ 60876 this.setAttributeConfig("MSG_LOADING", { 60877 value: "Loading...", 60878 validator: lang.isString 60879 }); 60880 60881 /** 60882 * @attribute MSG_ERROR 60883 * @description Message to display while DataTable has data error. String 60884 * values are treated as markup and inserted into the DOM with innerHTML. 60885 * @type HTML 60886 * @default "Data error." 60887 */ 60888 this.setAttributeConfig("MSG_ERROR", { 60889 value: "Data error.", 60890 validator: lang.isString 60891 }); 60892 60893 /** 60894 * @attribute MSG_SORTASC
60895 * @description Message to display in tooltip to sort Column in ascending 60896 * order. String values are treated as markup and inserted into the DOM as 60897 * innerHTML. 60898 * @type HTML 60899 * @default "Click to sort ascending" 60900 */ 60901 this.setAttributeConfig("MSG_SORTASC", { 60902 value: "Click to sort ascending", 60903 validator: lang.isString, 60904 method: function(sParam) { 60905 if(this._elThead) { 60906 for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) { 60907 if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) { 60908 allKeys[i]._elThLabel.firstChild.title = sParam; 60909 } 60910 } 60911 } 60912 } 60913 }); 60914 60915 /** 60916 * @attribute MSG_SORTDESC 60917 * @description Message to display in tooltip to sort Column in descending 60918 * order. String values are treated as markup and inserted into the DOM as 60919 * innerHTML. 60920 * @type HTML 60921 * @default "Click to sort descending" 60922 */ 60923 this.setAttributeConfig("MSG_SORTDESC", { 60924 value: "Click to sort descending", 60925 validator: lang.isString, 60926 method: function(sParam) { 60927 if(this._elThead) { 60928 for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) { 60929 if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) { 60930 allKeys[i]._elThLabel.firstChild.title = sParam; 60931 } 60932 } 60933 } 60934 } 60935 }); 60936 60937 /** 60938 * @attribute currencySymbol 60939 * @deprecated Use currencyOptions. 60940 */ 60941 this.setAttributeConfig("currencySymbol", { 60942 value: "$", 60943 validator: lang.isString 60944 }); 60945 60946 /** 60947 * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter. 60948 * @attribute currencyOptions 60949 * @type Object 60950 * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","} 60951 */ 60952 this.setAttributeConfig("currencyOptions", { 60953 value: { 60954 prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol 60955 decimalPlaces:2, 60956 decimalSeparator:".", 60957 thousandsSeparator:"," 60958 } 60959 }); 60960 60961 /** 60962 * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter. 60963 * @attribute dateOptions 60964 * @type Object 60965 * @default {format:"%m/%d/%Y", locale:"en"} 60966 */ 60967 this.setAttributeConfig("dateOptions", { 60968 value: {format:"%m/%d/%Y", locale:"en"} 60969 }); 60970 60971 /** 60972 * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter. 60973 * @attribute numberOptions 60974 * @type Object 60975 * @default {decimalPlaces:0, thousandsSeparator:","} 60976 */ 60977 this.setAttributeConfig("numberOptions", { 60978 value: { 60979 decimalPlaces:0, 60980 thousandsSeparator:"," 60981 } 60982 }); 60983 60984}, 60985 60986///////////////////////////////////////////////////////////////////////////// 60987// 60988// Private member variables 60989// 60990///////////////////////////////////////////////////////////////////////////// 60991 60992/** 60993 * True if instance is initialized, so as to fire the initEvent after render. 60994 * 60995 * @property _bInit 60996 * @type Boolean 60997 * @default true 60998 * @private 60999 */ 61000_bInit : true, 61001 61002/** 61003 * Index assigned to instance. 61004 * 61005 * @property _nIndex 61006 * @type Number 61007 * @private 61008 */ 61009_nIndex : null, 61010 61011/** 61012 * Counter for IDs assigned to TR elements. 61013 * 61014 * @property _nTrCount 61015 * @type Number 61016 * @private 61017 */ 61018_nTrCount : 0, 61019 61020/** 61021 * Counter for IDs assigned to TD elements. 61022 * 61023 * @property _nTdCount 61024 * @type Number 61025 * @private 61026 */ 61027_nTdCount : 0, 61028 61029/** 61030 * Unique id assigned to instance "yui-dtN", useful prefix for generating unique 61031 * DOM ID strings and log messages. 61032 * 61033 * @property _sId 61034 * @type String 61035 * @private 61036 */ 61037_sId : null, 61038 61039/** 61040 * Render chain. 61041 * 61042 * @property _oChainRender 61043 * @type YAHOO.util.Chain 61044 * @private 61045 */ 61046_oChainRender : null, 61047 61048/** 61049 * DOM reference to the container element for the DataTable instance into which 61050 * all other elements get created. 61051 * 61052 * @property _elContainer 61053 * @type HTMLElement 61054 * @private 61055 */ 61056_elContainer : null, 61057 61058/** 61059 * DOM reference to the mask element for the DataTable instance which disables it. 61060 * 61061 * @property _elMask 61062 * @type HTMLElement 61063 * @private 61064 */ 61065_elMask : null, 61066 61067/** 61068 * DOM reference to the TABLE element for the DataTable instance. 61069 * 61070 * @property _elTable 61071 * @type HTMLElement 61072 * @private 61073 */ 61074_elTable : null, 61075 61076/** 61077 * DOM reference to the CAPTION element for the DataTable instance. 61078 * 61079 * @property _elCaption 61080 * @type HTMLElement 61081 * @private 61082 */ 61083_elCaption : null, 61084 61085/** 61086 * DOM reference to the COLGROUP element for the DataTable instance. 61087 * 61088 * @property _elColgroup 61089 * @type HTMLElement 61090 * @private 61091 */ 61092_elColgroup : null, 61093 61094/** 61095 * DOM reference to the THEAD element for the DataTable instance. 61096 * 61097 * @property _elThead 61098 * @type HTMLElement 61099 * @private 61100 */ 61101_elThead : null, 61102 61103/** 61104 * DOM reference to the primary TBODY element for the DataTable instance. 61105 * 61106 * @property _elTbody 61107 * @type HTMLElement 61108 * @private 61109 */ 61110_elTbody : null, 61111 61112/** 61113 * DOM reference to the secondary TBODY element used to display DataTable messages. 61114 * 61115 * @property _elMsgTbody 61116 * @type HTMLElement 61117 * @private 61118 */ 61119_elMsgTbody : null, 61120 61121/** 61122 * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages. 61123 * 61124 * @property _elMsgTr 61125 * @type HTMLElement 61126 * @private 61127 */ 61128_elMsgTr : null, 61129 61130/** 61131 * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages. 61132 * 61133 * @property _elMsgTd 61134 * @type HTMLElement 61135 * @private 61136 */ 61137_elMsgTd : null, 61138 61139/** 61140 * Element reference to shared Column drag target. 61141 * 61142 * @property _elColumnDragTarget 61143 * @type HTMLElement 61144 * @private 61145 */ 61146_elColumnDragTarget : null, 61147 61148/** 61149 * Element reference to shared Column resizer proxy. 61150 * 61151 * @property _elColumnResizerProxy 61152 * @type HTMLElement 61153 * @private 61154 */ 61155_elColumnResizerProxy : null, 61156 61157/** 61158 * DataSource instance for the DataTable instance. 61159 * 61160 * @property _oDataSource 61161 * @type YAHOO.util.DataSource 61162 * @private 61163 */ 61164_oDataSource : null, 61165 61166/** 61167 * ColumnSet instance for the DataTable instance. 61168 * 61169 * @property _oColumnSet 61170 * @type YAHOO.widget.ColumnSet 61171 * @private 61172 */ 61173_oColumnSet : null, 61174 61175/** 61176 * RecordSet instance for the DataTable instance. 61177 * 61178 * @property _oRecordSet 61179 * @type YAHOO.widget.RecordSet 61180 * @private 61181 */ 61182_oRecordSet : null, 61183 61184/** 61185 * The active CellEditor instance for the DataTable instance. 61186 * 61187 * @property _oCellEditor 61188 * @type YAHOO.widget.CellEditor 61189 * @private 61190 */ 61191_oCellEditor : null, 61192 61193/** 61194 * ID string of first TR element of the current DataTable page. 61195 * 61196 * @property _sFirstTrId
61197 * @type String 61198 * @private 61199 */ 61200_sFirstTrId : null, 61201 61202/** 61203 * ID string of the last TR element of the current DataTable page. 61204 * 61205 * @property _sLastTrId 61206 * @type String 61207 * @private 61208 */ 61209_sLastTrId : null, 61210 61211/** 61212 * Template row to create all new rows from. 61213 * @property _elTrTemplate 61214 * @type {HTMLElement} 61215 * @private 61216 */ 61217_elTrTemplate : null, 61218 61219/** 61220 * Sparse array of custom functions to set column widths for browsers that don't 61221 * support dynamic CSS rules. Functions are added at the index representing 61222 * the number of rows they update. 61223 * 61224 * @property _aDynFunctions 61225 * @type Array 61226 * @private 61227 */ 61228_aDynFunctions : [], 61229 61230/** 61231 * Disabled state. 61232 * 61233 * @property _disabled 61234 * @type Boolean 61235 * @private 61236 */ 61237_disabled : false, 61238 61239 61240 61241 61242 61243 61244 61245 61246 61247 61248 61249 61250 61251 61252 61253 61254 61255 61256 61257 61258 61259 61260 61261 61262 61263 61264 61265 61266///////////////////////////////////////////////////////////////////////////// 61267// 61268// Private methods 61269// 61270///////////////////////////////////////////////////////////////////////////// 61271 61272/** 61273 * Clears browser text selection. Useful to call on rowSelectEvent or 61274 * cellSelectEvent to prevent clicks or dblclicks from selecting text in the 61275 * browser. 61276 * 61277 * @method clearTextSelection 61278 */ 61279clearTextSelection : function() { 61280 var sel; 61281 if(window.getSelection) { 61282 sel = window.getSelection(); 61283 } 61284 else if(document.getSelection) { 61285 sel = document.getSelection(); 61286 } 61287 else if(document.selection) { 61288 sel = document.selection; 61289 } 61290 if(sel) { 61291 if(sel.empty) { 61292 sel.empty(); 61293 } 61294 else if (sel.removeAllRanges) { 61295 sel.removeAllRanges(); 61296 } 61297 else if(sel.collapse) { 61298 sel.collapse(); 61299 } 61300 } 61301}, 61302 61303/** 61304 * Sets focus on the given element. 61305 * 61306 * @method _focusEl 61307 * @param el {HTMLElement} Element. 61308 * @private 61309 */ 61310_focusEl : function(el) { 61311 el = el || this._elTbody; 61312 // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets 61313 // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing 61314 // strange unexpected things as the user clicks on buttons and other controls. 61315 setTimeout(function() { 61316 try { 61317 el.focus(); 61318 } 61319 catch(e) { 61320 } 61321 },0); 61322}, 61323 61324/** 61325 * Forces Gecko repaint. 61326 * 61327 * @method _repaintGecko 61328 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body. 61329 * @private 61330 */ 61331_repaintGecko : (ua.gecko) ? 61332 function(el) { 61333 el = el || this._elContainer; 61334 var parent = el.parentNode; 61335 var nextSibling = el.nextSibling; 61336 parent.insertBefore(parent.removeChild(el), nextSibling); 61337 } : function() {}, 61338 61339/** 61340 * Forces Opera repaint. 61341 * 61342 * @method _repaintOpera 61343 * @private 61344 */ 61345_repaintOpera : (ua.opera) ? 61346 function() { 61347 if(ua.opera) { 61348 document.documentElement.className += " "; 61349 document.documentElement.className = YAHOO.lang.trim(document.documentElement.className); 61350 } 61351 } : function() {} , 61352 61353/** 61354 * Forces Webkit repaint. 61355 * 61356 * @method _repaintWebkit 61357 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body. 61358 * @private 61359 */ 61360_repaintWebkit : (ua.webkit) ? 61361 function(el) { 61362 el = el || this._elContainer; 61363 var parent = el.parentNode; 61364 var nextSibling = el.nextSibling; 61365 parent.insertBefore(parent.removeChild(el), nextSibling); 61366 } : function() {}, 61367 61368 61369 61370 61371 61372 61373 61374 61375 61376 61377 61378 61379 61380 61381 61382 61383 61384 61385 61386 61387 61388 61389// INIT FUNCTIONS 61390 61391/** 61392 * Initializes object literal of config values. 61393 * 61394 * @method _initConfigs 61395 * @param oConfig {Object} Object literal of config values. 61396 * @private 61397 */ 61398_initConfigs : function(oConfigs) { 61399 if(!oConfigs || !lang.isObject(oConfigs)) { 61400 oConfigs = {}; 61401 } 61402 this.configs = oConfigs; 61403}, 61404 61405/** 61406 * Initializes ColumnSet. 61407 * 61408 * @method _initColumnSet 61409 * @param aColumnDefs {Object[]} Array of object literal Column definitions. 61410 * @private 61411 */ 61412_initColumnSet : function(aColumnDefs) { 61413 var oColumn, i, len; 61414 61415 if(this._oColumnSet) { 61416 // First clear _oDynStyles for existing ColumnSet and 61417 // uregister CellEditor Custom Events 61418 for(i=0, len=this._oColumnSet.keys.length; i<len; i++) { 61419 oColumn = this._oColumnSet.keys[i]; 61420 DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined; 61421 if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility 61422 oColumn.editor.unsubscribeAll(); 61423 } 61424 } 61425 61426 this._oColumnSet = null; 61427 this._clearTrTemplateEl(); 61428 } 61429 61430 if(lang.isArray(aColumnDefs)) { 61431 this._oColumnSet = new YAHOO.widget.ColumnSet(aColumnDefs); 61432 } 61433 // Backward compatibility 61434 else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) { 61435 this._oColumnSet = aColumnDefs; 61436 YAHOO.log("DataTable's constructor now requires an array" + 61437 " of object literal Column definitions instead of a ColumnSet instance", 61438 "warn", this.toString()); 61439 } 61440 61441 // Register CellEditor Custom Events 61442 var allKeys = this._oColumnSet.keys; 61443 for(i=0, len=allKeys.length; i<len; i++) {
61444 oColumn = allKeys[i]; 61445 if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility 61446 oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true); 61447 oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true); 61448 oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true); 61449 oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true); 61450 oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true); 61451 oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true); 61452 oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true); 61453 oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true); 61454 } 61455 } 61456}, 61457 61458/** 61459 * Initializes DataSource. 61460 * 61461 * @method _initDataSource 61462 * @param oDataSource {YAHOO.util.DataSource} DataSource instance. 61463 * @private 61464 */ 61465_initDataSource : function(oDataSource) { 61466 this._oDataSource = null; 61467 if(oDataSource && (lang.isFunction(oDataSource.sendRequest))) { 61468 this._oDataSource = oDataSource; 61469 } 61470 // Backward compatibility 61471 else { 61472 var tmpTable = null; 61473 var tmpContainer = this._elContainer; 61474 var i=0; 61475 //TODO: this will break if re-initing DS at runtime for SDT 61476 // Peek in container child nodes to see if TABLE already exists 61477 if(tmpContainer.hasChildNodes()) { 61478 var tmpChildren = tmpContainer.childNodes; 61479 for(i=0; i<tmpChildren.length; i++) { 61480 if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") { 61481 tmpTable = tmpChildren[i]; 61482 break; 61483 } 61484 } 61485 if(tmpTable) { 61486 var tmpFieldsArray = []; 61487 for(; i<this._oColumnSet.keys.length; i++) { 61488 tmpFieldsArray.push({key:this._oColumnSet.keys[i].key}); 61489 } 61490 61491 this._oDataSource = new DS(tmpTable); 61492 this._oDataSource.responseType = DS.TYPE_HTMLTABLE; 61493 this._oDataSource.responseSchema = {fields: tmpFieldsArray}; 61494 YAHOO.log("Null DataSource for progressive enhancement from" + 61495 " markup has been deprecated", "warn", this.toString()); 61496 } 61497 } 61498 } 61499}, 61500 61501/** 61502 * Initializes RecordSet. 61503 * 61504 * @method _initRecordSet 61505 * @private 61506 */ 61507_initRecordSet : function() { 61508 if(this._oRecordSet) { 61509 this._oRecordSet.reset(); 61510 } 61511 else { 61512 this._oRecordSet = new YAHOO.widget.RecordSet(); 61513 } 61514}, 61515 61516/** 61517 * Initializes DOM elements. 61518 * 61519 * @method _initDomElements 61520 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 61521 * return {Boolean} False in case of error, otherwise true 61522 * @private 61523 */ 61524_initDomElements : function(elContainer) { 61525 // Outer container 61526 this._initContainerEl(elContainer); 61527 // TABLE 61528 this._initTableEl(this._elContainer); 61529 // COLGROUP 61530 this._initColgroupEl(this._elTable); 61531 // THEAD 61532 this._initTheadEl(this._elTable); 61533 61534 // Message TBODY 61535 this._initMsgTbodyEl(this._elTable); 61536 61537 // Primary TBODY 61538 this._initTbodyEl(this._elTable); 61539 61540 if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody) { 61541 return false; 61542 } 61543 else { 61544 return true; 61545 } 61546}, 61547 61548/** 61549 * Destroy's the DataTable outer container element, if available. 61550 * 61551 * @method _destroyContainerEl 61552 * @param elContainer {HTMLElement} Reference to the container element. 61553 * @private 61554 */ 61555_destroyContainerEl : function(elContainer) { 61556 var columns = this._oColumnSet.keys, 61557 elements, i; 61558 61559 Dom.removeClass(elContainer, DT.CLASS_DATATABLE); 61560 61561 // Bug 2528783 61562 Ev.purgeElement( elContainer ); 61563 Ev.purgeElement( this._elThead, true ); // recursive to get resize handles 61564 Ev.purgeElement( this._elTbody ); 61565 Ev.purgeElement( this._elMsgTbody ); 61566 61567 // because change doesn't bubble, each select (via formatDropdown) gets 61568 // its own subscription 61569 elements = elContainer.getElementsByTagName( 'select' ); 61570 61571 if ( elements.length ) { 61572 Ev.detachListener( elements, 'change' ); 61573 } 61574 61575 for ( i = columns.length - 1; i >= 0; --i ) { 61576 if ( columns[i].editor ) { 61577 Ev.purgeElement( columns[i].editor._elContainer ); 61578 } 61579 } 61580 61581 elContainer.innerHTML = ""; 61582 61583 this._elContainer = null; 61584 this._elColgroup = null; 61585 this._elThead = null; 61586 this._elTbody = null; 61587}, 61588 61589/** 61590 * Initializes the DataTable outer container element, including a mask. 61591 * 61592 * @method _initContainerEl 61593 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 61594 * @private 61595 */ 61596_initContainerEl : function(elContainer) { 61597 // Validate container 61598 elContainer = Dom.get(elContainer); 61599 61600 if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) { 61601 // Destroy previous 61602 this._destroyContainerEl(elContainer); 61603 61604 Dom.addClass(elContainer, DT.CLASS_DATATABLE);
61605 Ev.addListener(elContainer, "focus", this._onTableFocus, this); 61606 Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this); 61607 this._elContainer = elContainer; 61608 61609 var elMask = document.createElement("div"); 61610 elMask.className = DT.CLASS_MASK; 61611 elMask.style.display = "none"; 61612 this._elMask = elContainer.appendChild(elMask); 61613 } 61614}, 61615 61616/** 61617 * Destroy's the DataTable TABLE element, if available. 61618 * 61619 * @method _destroyTableEl 61620 * @private 61621 */ 61622_destroyTableEl : function() { 61623 var elTable = this._elTable; 61624 if(elTable) { 61625 Ev.purgeElement(elTable, true); 61626 elTable.parentNode.removeChild(elTable); 61627 this._elCaption = null; 61628 this._elColgroup = null; 61629 this._elThead = null; 61630 this._elTbody = null; 61631 } 61632}, 61633 61634/** 61635 * Creates HTML markup CAPTION element. 61636 * 61637 * @method _initCaptionEl 61638 * @param sCaption {HTML} Caption value. String values are treated as markup and 61639 * inserted into the DOM with innerHTML. 61640 * @private 61641 */ 61642_initCaptionEl : function(sCaption) { 61643 if(this._elTable && sCaption) { 61644 // Create CAPTION element 61645 if(!this._elCaption) { 61646 this._elCaption = this._elTable.createCaption(); 61647 } 61648 // Set CAPTION value 61649 this._elCaption.innerHTML = sCaption; 61650 } 61651 else if(this._elCaption) { 61652 this._elCaption.parentNode.removeChild(this._elCaption); 61653 } 61654}, 61655 61656/** 61657 * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer 61658 * container element. 61659 * 61660 * @method _initTableEl 61661 * @param elContainer {HTMLElement} Container element into which to create TABLE. 61662 * @private 61663 */ 61664_initTableEl : function(elContainer) { 61665 if(elContainer) { 61666 // Destroy previous 61667 this._destroyTableEl(); 61668 61669 // Create TABLE 61670 this._elTable = elContainer.appendChild(document.createElement("table")); 61671 61672 // Set SUMMARY attribute 61673 this._elTable.summary = this.get("summary"); 61674 61675 // Create CAPTION element 61676 if(this.get("caption")) { 61677 this._initCaptionEl(this.get("caption")); 61678 } 61679 61680 // Set up mouseover/mouseout events via mouseenter/mouseleave delegation 61681 Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this); 61682 Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this); 61683 Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-data>tr>td", this); 61684 Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-data>tr>td", this); 61685 Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-message>tr>td", this); 61686 Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-message>tr>td", this); 61687 } 61688}, 61689 61690/** 61691 * Destroy's the DataTable COLGROUP element, if available. 61692 * 61693 * @method _destroyColgroupEl 61694 * @private 61695 */ 61696_destroyColgroupEl : function() { 61697 var elColgroup = this._elColgroup; 61698 if(elColgroup) { 61699 var elTable = elColgroup.parentNode; 61700 Ev.purgeElement(elColgroup, true); 61701 elTable.removeChild(elColgroup); 61702 this._elColgroup = null; 61703 } 61704}, 61705 61706/** 61707 * Initializes COLGROUP and COL elements for managing minWidth. 61708 * 61709 * @method _initColgroupEl 61710 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP. 61711 * @private 61712 */ 61713_initColgroupEl : function(elTable) { 61714 if(elTable) { 61715 // Destroy previous 61716 this._destroyColgroupEl(); 61717 61718 // Add COLs to DOCUMENT FRAGMENT 61719 var allCols = this._aColIds || [], 61720 allKeys = this._oColumnSet.keys, 61721 i = 0, len = allCols.length, 61722 elCol, oColumn, 61723 elFragment = document.createDocumentFragment(), 61724 elColTemplate = document.createElement("col"); 61725 61726 for(i=0,len=allKeys.length; i<len; i++) { 61727 oColumn = allKeys[i]; 61728 elCol = elFragment.appendChild(elColTemplate.cloneNode(false)); 61729 } 61730 61731 // Create COLGROUP 61732 var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild); 61733 elColgroup.appendChild(elFragment); 61734 this._elColgroup = elColgroup; 61735 } 61736}, 61737 61738/** 61739 * Adds a COL element to COLGROUP at given index. 61740 * 61741 * @method _insertColgroupColEl 61742 * @param index {Number} Index of new COL element. 61743 * @private 61744 */ 61745_insertColgroupColEl : function(index) { 61746 if(lang.isNumber(index)&& this._elColgroup) { 61747 var nextSibling = this._elColgroup.childNodes[index] || null; 61748 this._elColgroup.insertBefore(document.createElement("col"), nextSibling); 61749 } 61750}, 61751 61752/** 61753 * Removes a COL element to COLGROUP at given index. 61754 * 61755 * @method _removeColgroupColEl 61756 * @param index {Number} Index of removed COL element. 61757 * @private 61758 */ 61759_removeColgroupColEl : function(index) { 61760 if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) { 61761 this._elColgroup.removeChild(this._elColgroup.childNodes[index]); 61762 } 61763}, 61764 61765/** 61766 * Reorders a COL element from old index(es) to new index. 61767 * 61768 * @method _reorderColgroupColEl 61769 * @param aKeyIndexes {Number[]} Array of indexes of removed COL element. 61770 * @param newIndex {Number} New index. 61771 * @private 61772 */ 61773_reorderColgroupColEl : function(aKeyIndexes, newIndex) { 61774 if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) { 61775 var i, 61776 tmpCols = []; 61777 // Remove COL 61778 for(i=aKeyIndexes.length-1; i>-1; i--) { 61779 tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]])); 61780 } 61781 // Insert COL 61782 var nextSibling = this._elColgroup.childNodes[newIndex] || null; 61783 for(i=tmpCols.length-1; i>-1; i--) { 61784 this._elColgroup.insertBefore(tmpCols[i], nextSibling); 61785 } 61786 } 61787}, 61788 61789/** 61790 * Destroy's the DataTable THEAD element, if available. 61791 * 61792 * @method _destroyTheadEl 61793 * @private 61794 */ 61795_destroyTheadEl : function() { 61796 var elThead = this._elThead; 61797 if(elThead) { 61798 var elTable = elThead.parentNode; 61799 Ev.purgeElement(elThead, true); 61800 this._destroyColumnHelpers(); 61801 elTable.removeChild(elThead); 61802 this._elThead = null; 61803 } 61804}, 61805 61806/** 61807 * Initializes THEAD element. 61808 * 61809 * @method _initTheadEl 61810 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP. 61811 * @param {HTMLElement} Initialized THEAD element. 61812 * @private 61813 */ 61814_initTheadEl : function(elTable) { 61815 elTable = elTable || this._elTable; 61816 61817 if(elTable) { 61818 // Destroy previous 61819 this._destroyTheadEl(); 61820 61821 //TODO: append to DOM later for performance 61822 var elThead = (this._elColgroup) ? 61823 elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) : 61824 elTable.appendChild(document.createElement("thead")); 61825 61826 // Set up DOM events for THEAD 61827 Ev.addListener(elThead, "focus", this._onTheadFocus, this); 61828 Ev.addListener(elThead, "keydown", this._onTheadKeydown, this); 61829 Ev.addListener(elThead, "mousedown", this._onTableMousedown, this); 61830 Ev.addListener(elThead, "mouseup", this._onTableMouseup, this); 61831 Ev.addListener(elThead, "click", this._onTheadClick, this); 61832 61833 // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave 61834 // delegation at the TABLE level 61835 61836 // Since we can't listen for click and dblclick on the same element... 61837 // Attach separately to THEAD and TBODY 61838 ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this); 61839 61840 var oColumnSet = this._oColumnSet, 61841 oColumn, i,j, l; 61842 61843 // Add TRs to the THEAD 61844 var colTree = oColumnSet.tree; 61845 var elTh; 61846 for(i=0; i<colTree.length; i++) { 61847 var elTheadTr = elThead.appendChild(document.createElement("tr")); 61848 61849 // ...and create TH cells 61850 for(j=0; j<colTree[i].length; j++) { 61851 oColumn = colTree[i][j]; 61852 elTh = elTheadTr.appendChild(document.createElement("th")); 61853 this._initThEl(elTh,oColumn); 61854 } 61855 61856 // Set FIRST/LAST on THEAD rows 61857 if(i === 0) { 61858 Dom.addClass(elTheadTr, DT.CLASS_FIRST); 61859 } 61860 if(i === (colTree.length-1)) { 61861 Dom.addClass(elTheadTr, DT.CLASS_LAST); 61862 } 61863 61864 } 61865 61866 // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array 61867 var aFirstHeaders = oColumnSet.headers[0] || []; 61868 for(i=0; i<aFirstHeaders.length; i++) { 61869 Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST); 61870 } 61871 var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || []; 61872 for(i=0; i<aLastHeaders.length; i++) { 61873 Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST); 61874 } 61875 61876 YAHOO.log("TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString()); 61877 61878 ///TODO: try _repaintGecko(this._elContainer) instead 61879 // Bug 1806891 61880 if(ua.webkit && ua.webkit < 420) { 61881 var oSelf = this; 61882 setTimeout(function() { 61883 elThead.style.display = ""; 61884 },0); 61885 elThead.style.display = 'none'; 61886 } 61887 61888 this._elThead = elThead; 61889 61890 // Column helpers needs _elThead to exist 61891 this._initColumnHelpers(); 61892 } 61893}, 61894 61895/** 61896 * Populates TH element as defined by Column. 61897 * 61898 * @method _initThEl 61899 * @param elTh {HTMLElement} TH element reference. 61900 * @param oColumn {YAHOO.widget.Column} Column object. 61901 * @private 61902 */ 61903_initThEl : function(elTh, oColumn) { 61904 elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD 61905 elTh.innerHTML = ""; 61906 elTh.rowSpan = oColumn.getRowspan(); 61907 elTh.colSpan = oColumn.getColspan(); 61908 oColumn._elTh = elTh; 61909 61910 var elThLiner = elTh.appendChild(document.createElement("div")); 61911 elThLiner.id = elTh.id + "-liner"; // Needed for resizer 61912 elThLiner.className = DT.CLASS_LINER; 61913 oColumn._elThLiner = elThLiner; 61914 61915 var elThLabel = elThLiner.appendChild(document.createElement("span")); 61916 elThLabel.className = DT.CLASS_LABEL; 61917 61918 // Assign abbr attribute 61919 if(oColumn.abbr) { 61920 elTh.abbr = oColumn.abbr; 61921 } 61922 // Clear minWidth on hidden Columns 61923 if(oColumn.hidden) { 61924 this._clearMinWidth(oColumn); 61925 } 61926 61927 elTh.className = this._getColumnClassNames(oColumn); 61928 61929 // Set Column width... 61930 if(oColumn.width) { 61931 // Validate minWidth 61932 var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
61933 oColumn.minWidth : oColumn.width; 61934 // ...for fallback cases 61935 if(DT._bDynStylesFallback) { 61936 elTh.firstChild.style.overflow = 'hidden'; 61937 elTh.firstChild.style.width = nWidth + 'px'; 61938 } 61939 // ...for non fallback cases 61940 else { 61941 this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden'); 61942 } 61943 } 61944 61945 this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy")); 61946 if (oColumn.sortable) 61947 { 61948 oColumn.getThEl().setAttribute("aria-sort","none"); 61949 } 61950 oColumn._elThLabel = elThLabel; 61951}, 61952 61953/** 61954 * Outputs markup into the given TH based on given Column. 61955 * 61956 * @method formatTheadCell 61957 * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner, 61958 * not the liner DIV element. 61959 * @param oColumn {YAHOO.widget.Column} Column instance. 61960 * @param oSortedBy {Object} Sort state object literal. 61961*/ 61962formatTheadCell : function(elCellLabel, oColumn, oSortedBy) { 61963 var sKey = oColumn.getKey(); 61964 var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey; 61965 61966 // Add accessibility link for sortable Columns 61967 if(oColumn.sortable) { 61968 // Calculate the direction 61969 var sSortClass = this.getColumnSortDir(oColumn, oSortedBy); 61970 var bDesc = (sSortClass === DT.CLASS_DESC); 61971 61972 // This is the sorted Column 61973 if(oSortedBy && (oColumn.key === oSortedBy.key)) { 61974 bDesc = !(oSortedBy.dir === DT.CLASS_DESC); 61975 } 61976 61977 // Generate a unique HREF for visited status 61978 var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey(); 61979 61980 // Generate a dynamic TITLE for sort status 61981 var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC"); 61982 61983 // Format the element 61984 elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>"; 61985 } 61986 // Just display the label for non-sortable Columns 61987 else { 61988 elCellLabel.innerHTML = sLabel; 61989 } 61990}, 61991 61992/** 61993 * Disables DD from top-level Column TH elements. 61994 * 61995 * @method _destroyDraggableColumns 61996 * @private 61997 */ 61998_destroyDraggableColumns : function() { 61999 var oColumn, elTh; 62000 for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) { 62001 oColumn = this._oColumnSet.tree[0][i]; 62002 if(oColumn._dd) { 62003 oColumn._dd = oColumn._dd.unreg(); 62004 Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE); 62005 } 62006 } 62007 62008 // Destroy column drag proxy 62009 this._destroyColumnDragTargetEl(); 62010}, 62011 62012/** 62013 * Initializes top-level Column TH elements into DD instances. 62014 * 62015 * @method _initDraggableColumns 62016 * @private 62017 */ 62018_initDraggableColumns : function() { 62019 this._destroyDraggableColumns(); 62020 if(util.DD) { 62021 var oColumn, elTh, elDragTarget; 62022 for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) { 62023 oColumn = this._oColumnSet.tree[0][i]; 62024 elTh = oColumn.getThEl(); 62025 Dom.addClass(elTh, DT.CLASS_DRAGGABLE); 62026 elDragTarget = this._initColumnDragTargetEl(); 62027 oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget); 62028 } 62029 } 62030 else { 62031 YAHOO.log("Could not find DragDrop for draggable Columns", "warn", this.toString()); 62032 } 62033}, 62034 62035/** 62036 * Destroys shared Column drag target. 62037 * 62038 * @method _destroyColumnDragTargetEl 62039 * @private 62040 */ 62041_destroyColumnDragTargetEl : function() { 62042 if(this._elColumnDragTarget) { 62043 var el = this._elColumnDragTarget; 62044 YAHOO.util.Event.purgeElement(el); 62045 el.parentNode.removeChild(el); 62046 this._elColumnDragTarget = null; 62047 } 62048}, 62049 62050/** 62051 * Creates HTML markup for shared Column drag target. 62052 * 62053 * @method _initColumnDragTargetEl 62054 * @return {HTMLElement} Reference to Column drag target. 62055 * @private 62056 */ 62057_initColumnDragTargetEl : function() { 62058 if(!this._elColumnDragTarget) { 62059 // Attach Column drag target element as first child of body 62060 var elColumnDragTarget = document.createElement('div'); 62061 elColumnDragTarget.id = this.getId() + "-coltarget"; 62062 elColumnDragTarget.className = DT.CLASS_COLTARGET; 62063 elColumnDragTarget.style.display = "none"; 62064 document.body.insertBefore(elColumnDragTarget, document.body.firstChild); 62065 62066 // Internal tracker of Column drag target 62067 this._elColumnDragTarget = elColumnDragTarget; 62068 62069 } 62070 return this._elColumnDragTarget; 62071}, 62072 62073/** 62074 * Disables resizeability on key Column TH elements. 62075 * 62076 * @method _destroyResizeableColumns 62077 * @private 62078 */ 62079_destroyResizeableColumns : function() { 62080 var aKeys = this._oColumnSet.keys; 62081 for(var i=0, len=aKeys.length; i<len; i++) { 62082 if(aKeys[i]._ddResizer) { 62083 aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg(); 62084 Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE); 62085 } 62086 } 62087 62088 // Destroy resizer proxy 62089 this._destroyColumnResizerProxyEl(); 62090}, 62091 62092/** 62093 * Initializes resizeability on key Column TH elements. 62094 * 62095 * @method _initResizeableColumns 62096 * @private 62097 */ 62098_initResizeableColumns : function() { 62099 this._destroyResizeableColumns(); 62100 if(util.DD) { 62101 var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick; 62102 for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) { 62103 oColumn = this._oColumnSet.keys[i]; 62104 if(oColumn.resizeable) { 62105 elTh = oColumn.getThEl(); 62106 Dom.addClass(elTh, DT.CLASS_RESIZEABLE); 62107 elThLiner = oColumn.getThLinerEl(); 62108
62109 // Bug 1915349: So resizer is as tall as TH when rowspan > 1 62110 // Create a separate resizer liner with position:relative 62111 elThResizerLiner = elTh.appendChild(document.createElement("div")); 62112 elThResizerLiner.className = DT.CLASS_RESIZERLINER; 62113 62114 // Move TH contents into the new resizer liner 62115 elThResizerLiner.appendChild(elThLiner); 62116 62117 // Create the resizer 62118 elThResizer = elThResizerLiner.appendChild(document.createElement("div")); 62119 elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer 62120 elThResizer.className = DT.CLASS_RESIZER; 62121 oColumn._elResizer = elThResizer; 62122 62123 // Create the resizer proxy, once per instance 62124 elResizerProxy = this._initColumnResizerProxyEl(); 62125 oColumn._ddResizer = new YAHOO.util.ColumnResizer( 62126 this, oColumn, elTh, elThResizer, elResizerProxy); 62127 cancelClick = function(e) { 62128 Ev.stopPropagation(e); 62129 }; 62130 Ev.addListener(elThResizer,"click",cancelClick); 62131 } 62132 } 62133 } 62134 else { 62135 YAHOO.log("Could not find DragDrop for resizeable Columns", "warn", this.toString()); 62136 } 62137}, 62138 62139/** 62140 * Destroys shared Column resizer proxy. 62141 * 62142 * @method _destroyColumnResizerProxyEl 62143 * @return {HTMLElement} Reference to Column resizer proxy. 62144 * @private 62145 */ 62146_destroyColumnResizerProxyEl : function() { 62147 if(this._elColumnResizerProxy) { 62148 var el = this._elColumnResizerProxy; 62149 YAHOO.util.Event.purgeElement(el); 62150 el.parentNode.removeChild(el); 62151 this._elColumnResizerProxy = null; 62152 } 62153}, 62154 62155/** 62156 * Creates HTML markup for shared Column resizer proxy. 62157 * 62158 * @method _initColumnResizerProxyEl 62159 * @return {HTMLElement} Reference to Column resizer proxy. 62160 * @private 62161 */ 62162_initColumnResizerProxyEl : function() { 62163 if(!this._elColumnResizerProxy) { 62164 // Attach Column resizer element as first child of body 62165 var elColumnResizerProxy = document.createElement("div"); 62166 elColumnResizerProxy.id = this.getId() + "-colresizerproxy"; // Needed for ColumnResizer 62167 elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY; 62168 document.body.insertBefore(elColumnResizerProxy, document.body.firstChild); 62169 62170 // Internal tracker of Column resizer proxy 62171 this._elColumnResizerProxy = elColumnResizerProxy; 62172 } 62173 return this._elColumnResizerProxy; 62174}, 62175 62176/** 62177 * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers. 62178 * 62179 * @method _destroyColumnHelpers 62180 * @private 62181 */ 62182_destroyColumnHelpers : function() { 62183 this._destroyDraggableColumns(); 62184 this._destroyResizeableColumns(); 62185}, 62186 62187/** 62188 * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers. 62189 * 62190 * @method _initColumnHelpers 62191 * @private 62192 */ 62193_initColumnHelpers : function() { 62194 if(this.get("draggableColumns")) { 62195 this._initDraggableColumns(); 62196 } 62197 this._initResizeableColumns(); 62198}, 62199 62200/** 62201 * Destroy's the DataTable TBODY element, if available. 62202 * 62203 * @method _destroyTbodyEl 62204 * @private 62205 */ 62206_destroyTbodyEl : function() { 62207 var elTbody = this._elTbody; 62208 if(elTbody) { 62209 var elTable = elTbody.parentNode; 62210 Ev.purgeElement(elTbody, true); 62211 elTable.removeChild(elTbody); 62212 this._elTbody = null; 62213 } 62214}, 62215 62216/** 62217 * Initializes TBODY element for data. 62218 * 62219 * @method _initTbodyEl 62220 * @param elTable {HTMLElement} TABLE element into which to create TBODY . 62221 * @private 62222 */ 62223_initTbodyEl : function(elTable) { 62224 if(elTable) { 62225 // Destroy previous 62226 this._destroyTbodyEl(); 62227 62228 // Create TBODY 62229 var elTbody = elTable.appendChild(document.createElement("tbody")); 62230 elTbody.tabIndex = 0; 62231 elTbody.className = DT.CLASS_DATA; 62232 62233 // Set up DOM events for TBODY 62234 Ev.addListener(elTbody, "focus", this._onTbodyFocus, this); 62235 Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this); 62236 Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this); 62237 Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this); 62238 Ev.addListener(elTbody, "click", this._onTbodyClick, this); 62239 62240 // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave 62241 // delegation at the TABLE level 62242 62243 // Since we can't listen for click and dblclick on the same element... 62244 // Attach separately to THEAD and TBODY 62245 ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this); 62246 62247 62248 // IE puts focus outline in the wrong place 62249 if(ua.ie) { 62250 elTbody.hideFocus=true; 62251 } 62252 62253 this._elTbody = elTbody; 62254 } 62255}, 62256 62257/** 62258 * Destroy's the DataTable message TBODY element, if available. 62259 * 62260 * @method _destroyMsgTbodyEl 62261 * @private 62262 */ 62263_destroyMsgTbodyEl : function() { 62264 var elMsgTbody = this._elMsgTbody; 62265 if(elMsgTbody) { 62266 var elTable = elMsgTbody.parentNode; 62267 Ev.purgeElement(elMsgTbody, true); 62268 elTable.removeChild(elMsgTbody); 62269 this._elTbody = null; 62270 } 62271}, 62272 62273/** 62274 * Initializes TBODY element for messaging. 62275 * 62276 * @method _initMsgTbodyEl 62277 * @param elTable {HTMLElement} TABLE element into which to create TBODY 62278 * @private 62279 */ 62280_initMsgTbodyEl : function(elTable) { 62281 if(elTable) { 62282 var elMsgTbody = document.createElement("tbody"); 62283 elMsgTbody.className = DT.CLASS_MESSAGE; 62284 var elMsgTr = elMsgTbody.appendChild(document.createElement("tr")); 62285 elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST; 62286 this._elMsgTr = elMsgTr; 62287 var elMsgTd = elMsgTr.appendChild(document.createElement("td")); 62288 elMsgTd.colSpan = this._oColumnSet.keys.length || 1; 62289 elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST; 62290 this._elMsgTd = elMsgTd; 62291 elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody); 62292 var elMsgLiner = elMsgTd.appendChild(document.createElement("div")); 62293 elMsgLiner.className = DT.CLASS_LINER; 62294 this._elMsgTbody = elMsgTbody; 62295 62296 // Set up DOM events for TBODY 62297 Ev.addListener(elMsgTbody, "focus", this._onTbodyFocus, this); 62298 Ev.addListener(elMsgTbody, "mousedown", this._onTableMousedown, this); 62299 Ev.addListener(elMsgTbody, "mouseup", this._onTableMouseup, this); 62300 Ev.addListener(elMsgTbody, "keydown", this._onTbodyKeydown, this); 62301 Ev.addListener(elMsgTbody, "click", this._onTbodyClick, this); 62302 62303 // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave 62304 // delegation at the TABLE level 62305 } 62306}, 62307 62308/** 62309 * Initialize internal event listeners 62310 * 62311 * @method _initEvents 62312 * @private 62313 */ 62314_initEvents : function () { 62315 // Initialize Column sort 62316 this._initColumnSort(); 62317 62318 // Add the document level click listener 62319 YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this); 62320 62321 // Paginator integration 62322 this.subscribe("paginatorChange",function () { 62323 this._handlePaginatorChange.apply(this,arguments); 62324 }); 62325 62326 this.subscribe("initEvent",function () { 62327 this.renderPaginator(); 62328 }); 62329 62330 // Initialize CellEditor integration 62331 this._initCellEditing(); 62332}, 62333 62334/** 62335 * Initializes Column sorting. 62336 * 62337 * @method _initColumnSort 62338 * @private 62339 */ 62340_initColumnSort : function() { 62341 this.subscribe("theadCellClickEvent", this.onEventSortColumn); 62342 62343 // Backward compatibility 62344 var oSortedBy = this.get("sortedBy"); 62345 if(oSortedBy) { 62346 if(oSortedBy.dir == "desc") { 62347 this._configs.sortedBy.value.dir = DT.CLASS_DESC; 62348 } 62349 else if(oSortedBy.dir == "asc") { 62350 this._configs.sortedBy.value.dir = DT.CLASS_ASC; 62351 } 62352 } 62353}, 62354 62355/** 62356 * Initializes CellEditor integration. 62357 * 62358 * @method _initCellEditing 62359 * @private 62360 */ 62361_initCellEditing : function() { 62362 this.subscribe("editorBlurEvent",function () { 62363 this.onEditorBlurEvent.apply(this,arguments); 62364 }); 62365 this.subscribe("editorBlockEvent",function () { 62366 this.onEditorBlockEvent.apply(this,arguments); 62367 }); 62368 this.subscribe("editorUnblockEvent",function () { 62369 this.onEditorUnblockEvent.apply(this,arguments); 62370 }); 62371}, 62372 62373 62374 62375 62376 62377 62378 62379 62380 62381 62382 62383 62384 62385 62386 62387 62388 62389 62390 62391 62392 62393 62394 62395 62396 62397 62398 62399 62400 62401 62402 62403 62404 62405// DOM MUTATION FUNCTIONS 62406 62407/** 62408 * Retruns classnames to represent current Column states. 62409 * @method _getColumnClassnames 62410 * @param oColumn {YAHOO.widget.Column} Column instance. 62411 * @param aAddClasses {String[]} An array of additional classnames to add to the 62412 * return value. 62413 * @return {String} A String of classnames to be assigned to TH or TD elements 62414 * for given Column. 62415 * @private 62416 */ 62417_getColumnClassNames : function (oColumn, aAddClasses) { 62418 var allClasses; 62419 62420 // Add CSS classes 62421 if(lang.isString(oColumn.className)) { 62422 // Single custom class 62423 allClasses = [oColumn.className]; 62424 } 62425 else if(lang.isArray(oColumn.className)) { 62426 // Array of custom classes 62427 allClasses = oColumn.className; 62428 } 62429 else { 62430 // no custom classes 62431 allClasses = []; 62432 } 62433 62434 // Hook for setting width with via dynamic style uses key since ID is too disposable 62435 allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey(); 62436
62437 // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":" 62438 allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey(); 62439 62440 var isSortedBy = this.get("sortedBy") || {}; 62441 // Sorted 62442 if(oColumn.key === isSortedBy.key) { 62443 allClasses[allClasses.length] = isSortedBy.dir || ''; 62444 } 62445 // Hidden 62446 if(oColumn.hidden) { 62447 allClasses[allClasses.length] = DT.CLASS_HIDDEN; 62448 } 62449 // Selected 62450 if(oColumn.selected) { 62451 allClasses[allClasses.length] = DT.CLASS_SELECTED; 62452 } 62453 // Sortable 62454 if(oColumn.sortable) { 62455 allClasses[allClasses.length] = DT.CLASS_SORTABLE; 62456 } 62457 // Resizeable 62458 if(oColumn.resizeable) { 62459 allClasses[allClasses.length] = DT.CLASS_RESIZEABLE; 62460 } 62461 // Editable 62462 if(oColumn.editor) { 62463 allClasses[allClasses.length] = DT.CLASS_EDITABLE; 62464 } 62465 62466 // Addtnl classes, including First/Last 62467 if(aAddClasses) { 62468 allClasses = allClasses.concat(aAddClasses); 62469 } 62470 62471 return allClasses.join(' '); 62472}, 62473 62474/** 62475 * Clears TR element template in response to any Column state change. 62476 * @method _clearTrTemplateEl 62477 * @private 62478 */ 62479_clearTrTemplateEl : function () { 62480 this._elTrTemplate = null; 62481}, 62482 62483/** 62484 * Returns a new TR element template with TD elements classed with current 62485 * Column states. 62486 * @method _getTrTemplateEl 62487 * @return {HTMLElement} A TR element to be cloned and added to the DOM. 62488 * @private 62489 */ 62490_getTrTemplateEl : function (oRecord, index) { 62491 // Template is already available 62492 if(this._elTrTemplate) { 62493 return this._elTrTemplate; 62494 } 62495 // Template needs to be created 62496 else { 62497 var d = document, 62498 tr = d.createElement('tr'), 62499 td = d.createElement('td'), 62500 div = d.createElement('div'); 62501 62502 // Append the liner element 62503 td.appendChild(div); 62504 62505 // Create TD elements into DOCUMENT FRAGMENT 62506 var df = document.createDocumentFragment(), 62507 allKeys = this._oColumnSet.keys, 62508 elTd; 62509 62510 // Set state for each TD; 62511 var aAddClasses; 62512 for(var i=0, keysLen=allKeys.length; i<keysLen; i++) { 62513 // Clone the TD template 62514 elTd = td.cloneNode(true); 62515 62516 // Format the base TD 62517 elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1)); 62518 62519 df.appendChild(elTd); 62520 } 62521 tr.appendChild(df); 62522 tr.className = DT.CLASS_REC; 62523 this._elTrTemplate = tr; 62524 return tr; 62525 } 62526}, 62527 62528/** 62529 * Formats a basic TD element. 62530 * @method _formatTdEl 62531 * @param oColumn {YAHOO.widget.Column} Associated Column instance. 62532 * @param elTd {HTMLElement} An unformatted TD element. 62533 * @param index {Number} Column key index. 62534 * @param isLast {Boolean} True if Column is last key of the ColumnSet. 62535 * @return {HTMLElement} A formatted TD element. 62536 * @private 62537 */ 62538_formatTdEl : function (oColumn, elTd, index, isLast) { 62539 var oColumnSet = this._oColumnSet; 62540 62541 // Set the TD's accessibility headers 62542 var allHeaders = oColumnSet.headers, 62543 allColHeaders = allHeaders[index], 62544 sTdHeaders = "", 62545 sHeader; 62546 for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) { 62547 sHeader = this._sId + "-th-" + allColHeaders[j] + ' '; 62548 sTdHeaders += sHeader; 62549 } 62550 elTd.headers = sTdHeaders; 62551 62552 // Class the TD element 62553 var aAddClasses = []; 62554 if(index === 0) { 62555 aAddClasses[aAddClasses.length] = DT.CLASS_FIRST; 62556 } 62557 if(isLast) { 62558 aAddClasses[aAddClasses.length] = DT.CLASS_LAST; 62559 } 62560 elTd.className = this._getColumnClassNames(oColumn, aAddClasses); 62561 62562 // Class the liner element 62563 elTd.firstChild.className = DT.CLASS_LINER; 62564 62565 // Set Column width for fallback cases 62566 if(oColumn.width && DT._bDynStylesFallback) { 62567 // Validate minWidth 62568 var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ? 62569 oColumn.minWidth : oColumn.width; 62570 elTd.firstChild.style.overflow = 'hidden'; 62571 elTd.firstChild.style.width = nWidth + 'px'; 62572 } 62573 62574 return elTd; 62575}, 62576 62577 62578/** 62579 * Create a new TR element for a given Record and appends it with the correct 62580 * number of Column-state-classed TD elements. Striping is the responsibility of 62581 * the calling function, which may decide to stripe the single row, a subset of 62582 * rows, or all the rows.
62583 * @method _createTrEl 62584 * @param oRecord {YAHOO.widget.Record} Record instance 62585 * @return {HTMLElement} The new TR element. This must be added to the DOM. 62586 * @private 62587 */ 62588_addTrEl : function (oRecord) { 62589 var elTrTemplate = this._getTrTemplateEl(); 62590 62591 // Clone the TR template. 62592 var elTr = elTrTemplate.cloneNode(true); 62593 62594 // Populate content 62595 return this._updateTrEl(elTr,oRecord); 62596}, 62597 62598/** 62599 * Formats the contents of the given TR's TD elements with data from the given 62600 * Record. Only innerHTML should change, nothing structural. 62601 * 62602 * @method _updateTrEl 62603 * @param elTr {HTMLElement} The TR element to update. 62604 * @param oRecord {YAHOO.widget.Record} The associated Record instance. 62605 * @return {HTMLElement} DOM reference to the new TR element. 62606 * @private 62607 */ 62608_updateTrEl : function(elTr, oRecord) { 62609 var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true; 62610 if(ok) { 62611 // Hide the row to prevent constant reflows 62612 elTr.style.display = 'none'; 62613 62614 // Update TD elements with new data 62615 var allTds = elTr.childNodes, 62616 elTd; 62617 for(var i=0,len=allTds.length; i<len; ++i) { 62618 elTd = allTds[i]; 62619 62620 // Set the cell content 62621 this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]); 62622 } 62623 62624 // Redisplay the row for reflow 62625 elTr.style.display = ''; 62626 } 62627 62628 // Record-to-TR association and tracking of FIRST/LAST 62629 var oldId = elTr.id, 62630 newId = oRecord.getId(); 62631 if(this._sFirstTrId === oldId) { 62632 this._sFirstTrId = newId; 62633 } 62634 if(this._sLastTrId === oldId) { 62635 this._sLastTrId = newId; 62636 } 62637 elTr.id = newId; 62638 return elTr; 62639}, 62640 62641 62642/** 62643 * Deletes TR element by DOM reference or by DataTable page row index. 62644 * 62645 * @method _deleteTrEl 62646 * @param row {HTMLElement | Number} TR element reference or Datatable page row index. 62647 * @return {Boolean} Returns true if successful, else returns false. 62648 * @private 62649 */ 62650_deleteTrEl : function(row) { 62651 var rowIndex; 62652 62653 // Get page row index for the element 62654 if(!lang.isNumber(row)) { 62655 rowIndex = Dom.get(row).sectionRowIndex; 62656 } 62657 else { 62658 rowIndex = row; 62659 } 62660 if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) { 62661 // Cannot use tbody.deleteRow due to IE6 instability 62662 //return this._elTbody.deleteRow(rowIndex); 62663 return this._elTbody.removeChild(this._elTbody.rows[row]); 62664 } 62665 else { 62666 return null; 62667 } 62668}, 62669 62670 62671 62672 62673 62674 62675 62676 62677 62678 62679 62680 62681 62682 62683 62684 62685 62686 62687 62688 62689 62690 62691 62692 62693 62694 62695 62696// CSS/STATE FUNCTIONS 62697 62698 62699 62700 62701/** 62702 * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element 62703 * of the DataTable page and updates internal tracker. 62704 * 62705 * @method _unsetFirstRow 62706 * @private 62707 */ 62708_unsetFirstRow : function() { 62709 // Remove FIRST 62710 if(this._sFirstTrId) { 62711 Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST); 62712 this._sFirstTrId = null; 62713 } 62714}, 62715 62716/** 62717 * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element 62718 * of the DataTable page and updates internal tracker. 62719 * 62720 * @method _setFirstRow 62721 * @private 62722 */ 62723_setFirstRow : function() { 62724 this._unsetFirstRow(); 62725 var elTr = this.getFirstTrEl(); 62726 if(elTr) { 62727 // Set FIRST 62728 Dom.addClass(elTr, DT.CLASS_FIRST); 62729 this._sFirstTrId = elTr.id; 62730 } 62731}, 62732 62733/** 62734 * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element 62735 * of the DataTable page and updates internal tracker. 62736 * 62737 * @method _unsetLastRow 62738 * @private 62739 */ 62740_unsetLastRow : function() { 62741 // Unassign previous class 62742 if(this._sLastTrId) { 62743 Dom.removeClass(this._sLastTrId, DT.CLASS_LAST); 62744 this._sLastTrId = null; 62745 } 62746}, 62747 62748/** 62749 * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element 62750 * of the DataTable page and updates internal tracker. 62751 * 62752 * @method _setLastRow 62753 * @private 62754 */ 62755_setLastRow : function() { 62756 this._unsetLastRow(); 62757 var elTr = this.getLastTrEl(); 62758 if(elTr) { 62759 // Assign class 62760 Dom.addClass(elTr, DT.CLASS_LAST); 62761 this._sLastTrId = elTr.id; 62762 } 62763}, 62764 62765/** 62766 * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements. 62767 * 62768 * @method _setRowStripes 62769 * @param row {HTMLElement | String | Number} (optional) HTML TR element reference 62770 * or string ID, or page row index of where to start striping. 62771 * @param range {Number} (optional) If given, how many rows to stripe, otherwise 62772 * stripe all the rows until the end. 62773 * @private 62774 */ 62775_setRowStripes : function(row, range) { 62776 // Default values stripe all rows 62777 var allRows = this._elTbody.rows, 62778 nStartIndex = 0, 62779 nEndIndex = allRows.length, 62780 aOdds = [], nOddIdx = 0, 62781 aEvens = [], nEvenIdx = 0; 62782 62783 // Stripe a subset 62784 if((row !== null) && (row !== undefined)) { 62785 // Validate given start row 62786 var elStartRow = this.getTrEl(row); 62787 if(elStartRow) { 62788 nStartIndex = elStartRow.sectionRowIndex; 62789 62790 // Validate given range 62791 if(lang.isNumber(range) && (range > 1)) { 62792 nEndIndex = nStartIndex + range; 62793 } 62794 } 62795 } 62796 62797 for(var i=nStartIndex; i<nEndIndex; i++) { 62798 if(i%2) { 62799 aOdds[nOddIdx++] = allRows[i]; 62800 } else { 62801 aEvens[nEvenIdx++] = allRows[i]; 62802 } 62803 } 62804 62805 if (aOdds.length) { 62806 Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD); 62807 } 62808 62809 if (aEvens.length) { 62810 Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN); 62811 } 62812}, 62813 62814/** 62815 * Assigns the class DT.CLASS_SELECTED to TR and TD elements. 62816 * 62817 * @method _setSelections 62818 * @private 62819 */ 62820_setSelections : function() { 62821 // Keep track of selected rows
62822 var allSelectedRows = this.getSelectedRows(); 62823 // Keep track of selected cells 62824 var allSelectedCells = this.getSelectedCells(); 62825 // Anything to select? 62826 if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) { 62827 var oColumnSet = this._oColumnSet, 62828 el; 62829 // Loop over each row 62830 for(var i=0; i<allSelectedRows.length; i++) { 62831 el = Dom.get(allSelectedRows[i]); 62832 if(el) { 62833 Dom.addClass(el, DT.CLASS_SELECTED); 62834 } 62835 } 62836 // Loop over each cell 62837 for(i=0; i<allSelectedCells.length; i++) { 62838 el = Dom.get(allSelectedCells[i].recordId); 62839 if(el) { 62840 Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED); 62841 } 62842 } 62843 } 62844}, 62845 62846 62847 62848 62849 62850 62851 62852 62853 62854 62855 62856 62857 62858 62859 62860 62861 62862 62863 62864 62865 62866 62867 62868 62869 62870 62871 62872 62873 62874 62875 62876 62877 62878 62879 62880 62881 62882 62883 62884 62885 62886 62887 62888///////////////////////////////////////////////////////////////////////////// 62889// 62890// Private DOM Event Handlers 62891// 62892///////////////////////////////////////////////////////////////////////////// 62893 62894/** 62895 * Validates minWidths whenever the render chain ends. 62896 * 62897 * @method _onRenderChainEnd 62898 * @private 62899 */ 62900_onRenderChainEnd : function() { 62901 // Hide loading message 62902 this.hideTableMessage(); 62903 62904 // Show empty message 62905 if(this._elTbody.rows.length === 0) { 62906 this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY); 62907 } 62908 62909 // Execute in timeout thread to give implementers a chance 62910 // to subscribe after the constructor 62911 var oSelf = this; 62912 setTimeout(function() { 62913 if((oSelf instanceof DT) && oSelf._sId) { 62914 // Init event 62915 if(oSelf._bInit) { 62916 oSelf._bInit = false; 62917 oSelf.fireEvent("initEvent"); 62918 } 62919 62920 // Render event 62921 oSelf.fireEvent("renderEvent"); 62922 // Backward compatibility 62923 oSelf.fireEvent("refreshEvent"); 62924 YAHOO.log("DataTable rendered", "info", oSelf.toString()); 62925 62926 // Post-render routine 62927 oSelf.validateColumnWidths(); 62928 62929 // Post-render event 62930 oSelf.fireEvent("postRenderEvent"); 62931 62932 /*if(YAHOO.example.Performance.trialStart) { 62933 YAHOO.log((new Date()).getTime() - YAHOO.example.Performance.trialStart.getTime() + " ms", "time"); 62934 YAHOO.example.Performance.trialStart = null; 62935 }*/ 62936 62937 YAHOO.log("Post-render routine executed", "info", oSelf.toString()); 62938 } 62939 }, 0); 62940}, 62941 62942/** 62943 * Handles click events on the DOCUMENT. 62944 * 62945 * @method _onDocumentClick 62946 * @param e {HTMLEvent} The click event. 62947 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 62948 * @private 62949 */ 62950_onDocumentClick : function(e, oSelf) { 62951 var elTarget = Ev.getTarget(e); 62952 var elTag = elTarget.nodeName.toLowerCase(); 62953 62954 if(!Dom.isAncestor(oSelf._elContainer, elTarget)) { 62955 oSelf.fireEvent("tableBlurEvent"); 62956 62957 // Fires editorBlurEvent when click is not within the TABLE. 62958 // For cases when click is within the TABLE, due to timing issues, 62959 // the editorBlurEvent needs to get fired by the lower-level DOM click 62960 // handlers below rather than by the TABLE click handler directly. 62961 if(oSelf._oCellEditor) { 62962 if(oSelf._oCellEditor.getContainerEl) { 62963 var elContainer = oSelf._oCellEditor.getContainerEl(); 62964 // Only if the click was not within the CellEditor container 62965 if(!Dom.isAncestor(elContainer, elTarget) && 62966 (elContainer.id !== elTarget.id)) { 62967 oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor}); 62968 } 62969 } 62970 // Backward Compatibility 62971 else if(oSelf._oCellEditor.isActive) { 62972 // Only if the click was not within the Cell Editor container 62973 if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) && 62974 (oSelf._oCellEditor.container.id !== elTarget.id)) { 62975 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); 62976 } 62977 } 62978 } 62979 } 62980}, 62981 62982/** 62983 * Handles focus events on the DataTable instance. 62984 * 62985 * @method _onTableFocus 62986 * @param e {HTMLEvent} The focus event. 62987 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 62988 * @private 62989 */ 62990_onTableFocus : function(e, oSelf) { 62991 oSelf.fireEvent("tableFocusEvent"); 62992}, 62993 62994/** 62995 * Handles focus events on the THEAD element. 62996 * 62997 * @method _onTheadFocus 62998 * @param e {HTMLEvent} The focus event. 62999 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63000 * @private 63001 */ 63002_onTheadFocus : function(e, oSelf) { 63003 oSelf.fireEvent("theadFocusEvent"); 63004 oSelf.fireEvent("tableFocusEvent"); 63005}, 63006 63007/** 63008 * Handles focus events on the TBODY element. 63009 * 63010 * @method _onTbodyFocus 63011 * @param e {HTMLEvent} The focus event. 63012 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63013 * @private 63014 */ 63015_onTbodyFocus : function(e, oSelf) { 63016 oSelf.fireEvent("tbodyFocusEvent"); 63017 oSelf.fireEvent("tableFocusEvent"); 63018}, 63019 63020/** 63021 * Handles mouseover events on the DataTable instance. 63022 * 63023 * @method _onTableMouseover 63024 * @param e {HTMLEvent} The mouseover event. 63025 * @param origTarget {HTMLElement} The mouseenter delegated element. 63026 * @param container {HTMLElement} The mouseenter delegation container. 63027 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63028 * @private 63029 */ 63030_onTableMouseover : function(e, origTarget, container, oSelf) { 63031 var elTarget = origTarget; 63032 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63033 var bKeepBubbling = true; 63034 while(elTarget && (elTag != "table")) { 63035 switch(elTag) { 63036 case "body": 63037 return; 63038 case "a": 63039 break; 63040 case "td": 63041 bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e}); 63042 break; 63043 case "span":
63044 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63045 bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e}); 63046 // Backward compatibility 63047 bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e}); 63048 } 63049 break; 63050 case "th": 63051 bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e}); 63052 // Backward compatibility 63053 bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e}); 63054 break; 63055 case "tr": 63056 if(elTarget.parentNode.nodeName.toLowerCase() == "thead") { 63057 bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e}); 63058 // Backward compatibility 63059 bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e}); 63060 } 63061 else { 63062 bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e}); 63063 } 63064 break; 63065 default: 63066 break; 63067 } 63068 if(bKeepBubbling === false) { 63069 return; 63070 } 63071 else { 63072 elTarget = elTarget.parentNode; 63073 if(elTarget) { 63074 elTag = elTarget.nodeName.toLowerCase(); 63075 } 63076 } 63077 } 63078 oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63079}, 63080 63081/** 63082 * Handles mouseout events on the DataTable instance. 63083 * 63084 * @method _onTableMouseout 63085 * @param e {HTMLEvent} The mouseout event. 63086 * @param origTarget {HTMLElement} The mouseleave delegated element. 63087 * @param container {HTMLElement} The mouseleave delegation container. 63088 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63089 * @private 63090 */ 63091_onTableMouseout : function(e, origTarget, container, oSelf) { 63092 var elTarget = origTarget; 63093 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63094 var bKeepBubbling = true; 63095 while(elTarget && (elTag != "table")) { 63096 switch(elTag) { 63097 case "body": 63098 return; 63099 case "a": 63100 break; 63101 case "td": 63102 bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e}); 63103 break; 63104 case "span": 63105 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63106 bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e}); 63107 // Backward compatibility 63108 bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e}); 63109 } 63110 break; 63111 case "th": 63112 bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e}); 63113 // Backward compatibility 63114 bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e}); 63115 break; 63116 case "tr": 63117 if(elTarget.parentNode.nodeName.toLowerCase() == "thead") { 63118 bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e}); 63119 // Backward compatibility 63120 bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e}); 63121 } 63122 else { 63123 bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e}); 63124 } 63125 break; 63126 default: 63127 break; 63128 } 63129 if(bKeepBubbling === false) { 63130 return; 63131 } 63132 else { 63133 elTarget = elTarget.parentNode; 63134 if(elTarget) { 63135 elTag = elTarget.nodeName.toLowerCase(); 63136 } 63137 } 63138 } 63139 oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63140}, 63141 63142/** 63143 * Handles mousedown events on the DataTable instance. 63144 * 63145 * @method _onTableMousedown 63146 * @param e {HTMLEvent} The mousedown event. 63147 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63148 * @private 63149 */ 63150_onTableMousedown : function(e, oSelf) { 63151 var elTarget = Ev.getTarget(e); 63152 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63153 var bKeepBubbling = true; 63154 while(elTarget && (elTag != "table")) { 63155 switch(elTag) { 63156 case "body": 63157 return; 63158 case "a": 63159 break; 63160 case "td": 63161 bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e}); 63162 break; 63163 case "span":
63164 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63165 bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e}); 63166 // Backward compatibility 63167 bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e}); 63168 } 63169 break; 63170 case "th": 63171 bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e}); 63172 // Backward compatibility 63173 bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e}); 63174 break; 63175 case "tr": 63176 if(elTarget.parentNode.nodeName.toLowerCase() == "thead") { 63177 bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e}); 63178 // Backward compatibility 63179 bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e}); 63180 } 63181 else { 63182 bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e}); 63183 } 63184 break; 63185 default: 63186 break; 63187 } 63188 if(bKeepBubbling === false) { 63189 return; 63190 } 63191 else { 63192 elTarget = elTarget.parentNode; 63193 if(elTarget) { 63194 elTag = elTarget.nodeName.toLowerCase(); 63195 } 63196 } 63197 } 63198 oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63199}, 63200 63201/** 63202 * Handles mouseup events on the DataTable instance. 63203 * 63204 * @method _onTableMouseup 63205 * @param e {HTMLEvent} The mouseup event. 63206 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63207 * @private 63208 */ 63209_onTableMouseup : function(e, oSelf) { 63210 var elTarget = Ev.getTarget(e); 63211 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63212 var bKeepBubbling = true; 63213 while(elTarget && (elTag != "table")) { 63214 switch(elTag) { 63215 case "body": 63216 return; 63217 case "a": 63218 break; 63219 case "td": 63220 bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e}); 63221 break; 63222 case "span": 63223 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63224 bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e}); 63225 // Backward compatibility 63226 bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e}); 63227 } 63228 break; 63229 case "th": 63230 bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e}); 63231 // Backward compatibility 63232 bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e}); 63233 break; 63234 case "tr": 63235 if(elTarget.parentNode.nodeName.toLowerCase() == "thead") { 63236 bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e}); 63237 // Backward compatibility 63238 bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e}); 63239 } 63240 else { 63241 bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e}); 63242 } 63243 break; 63244 default: 63245 break; 63246 } 63247 if(bKeepBubbling === false) { 63248 return; 63249 } 63250 else { 63251 elTarget = elTarget.parentNode; 63252 if(elTarget) { 63253 elTag = elTarget.nodeName.toLowerCase(); 63254 } 63255 } 63256 } 63257 oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63258}, 63259 63260/** 63261 * Handles dblclick events on the DataTable instance. 63262 * 63263 * @method _onTableDblclick 63264 * @param e {HTMLEvent} The dblclick event. 63265 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63266 * @private 63267 */ 63268_onTableDblclick : function(e, oSelf) { 63269 var elTarget = Ev.getTarget(e); 63270 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63271 var bKeepBubbling = true; 63272 while(elTarget && (elTag != "table")) { 63273 switch(elTag) { 63274 case "body": 63275 return; 63276 case "td": 63277 bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e}); 63278 break; 63279 case "span":
63280 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63281 bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e}); 63282 // Backward compatibility 63283 bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e}); 63284 } 63285 break; 63286 case "th": 63287 bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e}); 63288 // Backward compatibility 63289 bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e}); 63290 break; 63291 case "tr": 63292 if(elTarget.parentNode.nodeName.toLowerCase() == "thead") { 63293 bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e}); 63294 // Backward compatibility 63295 bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e}); 63296 } 63297 else { 63298 bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e}); 63299 } 63300 break; 63301 default: 63302 break; 63303 } 63304 if(bKeepBubbling === false) { 63305 return; 63306 } 63307 else { 63308 elTarget = elTarget.parentNode; 63309 if(elTarget) { 63310 elTag = elTarget.nodeName.toLowerCase(); 63311 } 63312 } 63313 } 63314 oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63315}, 63316/** 63317 * Handles keydown events on the THEAD element. 63318 * 63319 * @method _onTheadKeydown 63320 * @param e {HTMLEvent} The key event. 63321 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63322 * @private 63323 */ 63324_onTheadKeydown : function(e, oSelf) { 63325 var elTarget = Ev.getTarget(e); 63326 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63327 var bKeepBubbling = true; 63328 while(elTarget && (elTag != "table")) { 63329 switch(elTag) { 63330 case "body": 63331 return; 63332 case "input": 63333 case "textarea": 63334 // TODO: implement textareaKeyEvent 63335 break; 63336 case "thead": 63337 bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e}); 63338 break; 63339 default: 63340 break; 63341 } 63342 if(bKeepBubbling === false) { 63343 return; 63344 } 63345 else { 63346 elTarget = elTarget.parentNode; 63347 if(elTarget) { 63348 elTag = elTarget.nodeName.toLowerCase(); 63349 } 63350 } 63351 } 63352 oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63353}, 63354 63355/** 63356 * Handles keydown events on the TBODY element. Handles selection behavior, 63357 * provides hooks for ENTER to edit functionality. 63358 * 63359 * @method _onTbodyKeydown 63360 * @param e {HTMLEvent} The key event. 63361 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63362 * @private 63363 */ 63364_onTbodyKeydown : function(e, oSelf) { 63365 var sMode = oSelf.get("selectionMode"); 63366 63367 if(sMode == "standard") { 63368 oSelf._handleStandardSelectionByKey(e); 63369 } 63370 else if(sMode == "single") { 63371 oSelf._handleSingleSelectionByKey(e); 63372 } 63373 else if(sMode == "cellblock") { 63374 oSelf._handleCellBlockSelectionByKey(e); 63375 } 63376 else if(sMode == "cellrange") { 63377 oSelf._handleCellRangeSelectionByKey(e); 63378 } 63379 else if(sMode == "singlecell") { 63380 oSelf._handleSingleCellSelectionByKey(e); 63381 } 63382 63383 if(oSelf._oCellEditor) { 63384 if(oSelf._oCellEditor.fireEvent) { 63385 oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor}); 63386 } 63387 else if(oSelf._oCellEditor.isActive) { 63388 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); 63389 } 63390 } 63391 63392 var elTarget = Ev.getTarget(e); 63393 var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(); 63394 var bKeepBubbling = true; 63395 while(elTarget && (elTag != "table")) { 63396 switch(elTag) { 63397 case "body": 63398 return; 63399 case "tbody": 63400 bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e}); 63401 break; 63402 default: 63403 break; 63404 } 63405 if(bKeepBubbling === false) { 63406 return; 63407 } 63408 else { 63409 elTarget = elTarget.parentNode; 63410 if(elTarget) { 63411 elTag = elTarget.nodeName.toLowerCase(); 63412 } 63413 } 63414 } 63415 oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63416}, 63417 63418/** 63419 * Handles click events on the THEAD element. 63420 * 63421 * @method _onTheadClick 63422 * @param e {HTMLEvent} The click event. 63423 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63424 * @private 63425 */ 63426_onTheadClick : function(e, oSelf) { 63427 // This blurs the CellEditor 63428 if(oSelf._oCellEditor) { 63429 if(oSelf._oCellEditor.fireEvent) { 63430 oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor}); 63431 } 63432 // Backward compatibility 63433 else if(oSelf._oCellEditor.isActive) { 63434 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); 63435 } 63436 } 63437 63438 var elTarget = Ev.getTarget(e), 63439 elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(), 63440 bKeepBubbling = true; 63441 while(elTarget && (elTag != "table")) { 63442 switch(elTag) { 63443 case "body": 63444 return; 63445 case "input": 63446 var sType = elTarget.type.toLowerCase(); 63447 if(sType == "checkbox") { 63448 bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e}); 63449 } 63450 else if(sType == "radio") { 63451 bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e}); 63452 } 63453 else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) { 63454 if(!elTarget.disabled) { 63455 bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e}); 63456 } 63457 else { 63458 bKeepBubbling = false;
63459 } 63460 } 63461 else if (elTarget.disabled){ 63462 bKeepBubbling = false; 63463 } 63464 break; 63465 case "a": 63466 bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e}); 63467 break; 63468 case "button": 63469 if(!elTarget.disabled) { 63470 bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e}); 63471 } 63472 else { 63473 bKeepBubbling = false; 63474 } 63475 break; 63476 case "span": 63477 if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) { 63478 bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e}); 63479 // Backward compatibility 63480 bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e}); 63481 } 63482 break; 63483 case "th": 63484 bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e}); 63485 // Backward compatibility 63486 bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e}); 63487 break; 63488 case "tr": 63489 bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e}); 63490 // Backward compatibility 63491 bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e}); 63492 break; 63493 default: 63494 break; 63495 } 63496 if(bKeepBubbling === false) { 63497 return; 63498 } 63499 else { 63500 elTarget = elTarget.parentNode; 63501 if(elTarget) { 63502 elTag = elTarget.nodeName.toLowerCase(); 63503 } 63504 } 63505 } 63506 oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63507}, 63508 63509/** 63510 * Handles click events on the primary TBODY element. 63511 * 63512 * @method _onTbodyClick 63513 * @param e {HTMLEvent} The click event. 63514 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63515 * @private 63516 */ 63517_onTbodyClick : function(e, oSelf) { 63518 // This blurs the CellEditor 63519 if(oSelf._oCellEditor) { 63520 if(oSelf._oCellEditor.fireEvent) { 63521 oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor}); 63522 } 63523 else if(oSelf._oCellEditor.isActive) { 63524 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); 63525 } 63526 } 63527 63528 // Fire Custom Events 63529 var elTarget = Ev.getTarget(e), 63530 elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(), 63531 bKeepBubbling = true; 63532 while(elTarget && (elTag != "table")) { 63533 switch(elTag) { 63534 case "body": 63535 return; 63536 case "input": 63537 var sType = elTarget.type.toLowerCase(); 63538 if(sType == "checkbox") { 63539 bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e}); 63540 } 63541 else if(sType == "radio") { 63542 bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e}); 63543 } 63544 else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) { 63545 if(!elTarget.disabled) { 63546 bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e}); 63547 } 63548 else { 63549 bKeepBubbling = false; 63550 } 63551 } 63552 else if (elTarget.disabled){ 63553 bKeepBubbling = false; 63554 } 63555 break; 63556 case "a": 63557 bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e}); 63558 break; 63559 case "button": 63560 if(!elTarget.disabled) { 63561 bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e}); 63562 } 63563 else { 63564 bKeepBubbling = false;
63565 } 63566 break; 63567 case "td": 63568 bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e}); 63569 break; 63570 case "tr": 63571 bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e}); 63572 break; 63573 default: 63574 break; 63575 } 63576 if(bKeepBubbling === false) { 63577 return; 63578 } 63579 else { 63580 elTarget = elTarget.parentNode; 63581 if(elTarget) { 63582 elTag = elTarget.nodeName.toLowerCase(); 63583 } 63584 } 63585 } 63586 oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e}); 63587}, 63588 63589/** 63590 * Handles change events on SELECT elements within DataTable. 63591 * 63592 * @method _onDropdownChange 63593 * @param e {HTMLEvent} The change event. 63594 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance. 63595 * @private 63596 */ 63597_onDropdownChange : function(e, oSelf) { 63598 var elTarget = Ev.getTarget(e); 63599 oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget}); 63600}, 63601 63602 63603 63604 63605 63606 63607 63608 63609 63610 63611 63612 63613 63614 63615 63616 63617 63618 63619 63620 63621 63622 63623 63624 63625 63626 63627 63628 63629 63630 63631 63632 63633///////////////////////////////////////////////////////////////////////////// 63634// 63635// Public member variables 63636// 63637///////////////////////////////////////////////////////////////////////////// 63638/** 63639 * Returns object literal of initial configs. 63640 * 63641 * @property configs 63642 * @type Object 63643 * @default {} 63644 */ 63645configs: null, 63646 63647 63648///////////////////////////////////////////////////////////////////////////// 63649// 63650// Public methods 63651// 63652///////////////////////////////////////////////////////////////////////////// 63653 63654/** 63655 * Returns unique id assigned to instance, which is a useful prefix for 63656 * generating unique DOM ID strings. 63657 * 63658 * @method getId 63659 * @return {String} Unique ID of the DataSource instance. 63660 */ 63661getId : function() { 63662 return this._sId; 63663}, 63664 63665/** 63666 * DataSource instance name, for logging. 63667 * 63668 * @method toString 63669 * @return {String} Unique name of the DataSource instance. 63670 */ 63671 63672toString : function() { 63673 return "DataTable instance " + this._sId; 63674}, 63675 63676/** 63677 * Returns the DataTable instance's DataSource instance. 63678 * 63679 * @method getDataSource 63680 * @return {YAHOO.util.DataSource} DataSource instance. 63681 */ 63682getDataSource : function() { 63683 return this._oDataSource; 63684}, 63685 63686/** 63687 * Returns the DataTable instance's ColumnSet instance. 63688 * 63689 * @method getColumnSet 63690 * @return {YAHOO.widget.ColumnSet} ColumnSet instance. 63691 */ 63692getColumnSet : function() { 63693 return this._oColumnSet; 63694}, 63695 63696/** 63697 * Returns the DataTable instance's RecordSet instance. 63698 * 63699 * @method getRecordSet 63700 * @return {YAHOO.widget.RecordSet} RecordSet instance. 63701 */ 63702getRecordSet : function() { 63703 return this._oRecordSet; 63704}, 63705 63706/** 63707 * Returns on object literal representing the DataTable instance's current 63708 * state with the following properties: 63709 * <dl> 63710 * <dt>pagination</dt> 63711 * <dd>Instance of YAHOO.widget.Paginator</dd> 63712 * 63713 * <dt>sortedBy</dt> 63714 * <dd> 63715 * <dl> 63716 * <dt>sortedBy.key</dt> 63717 * <dd>{String} Key of sorted Column</dd> 63718 * <dt>sortedBy.dir</dt> 63719 * <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd> 63720 * </dl> 63721 * </dd> 63722 * 63723 * <dt>selectedRows</dt> 63724 * <dd>Array of selected rows by Record ID.</dd> 63725 * 63726 * <dt>selectedCells</dt> 63727 * <dd>Selected cells as an array of object literals: 63728 * {recordId:sRecordId, columnKey:sColumnKey}</dd> 63729 * </dl> 63730 * 63731 * @method getState 63732 * @return {Object} DataTable instance state object literal values. 63733 */ 63734getState : function() { 63735 return { 63736 totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(), 63737 pagination: this.get("paginator") ? this.get("paginator").getState() : null, 63738 sortedBy: this.get("sortedBy"), 63739 selectedRows: this.getSelectedRows(), 63740 selectedCells: this.getSelectedCells() 63741 }; 63742}, 63743 63744 63745 63746 63747 63748 63749 63750 63751 63752 63753 63754 63755 63756 63757 63758 63759 63760 63761 63762 63763 63764 63765 63766 63767 63768 63769 63770 63771 63772 63773 63774 63775 63776 63777 63778 63779 63780 63781 63782 63783 63784 63785 63786// DOM ACCESSORS 63787 63788/** 63789 * Returns DOM reference to the DataTable's container element. 63790 * 63791 * @method getContainerEl 63792 * @return {HTMLElement} Reference to DIV element. 63793 */ 63794getContainerEl : function() { 63795 return this._elContainer; 63796}, 63797 63798/** 63799 * Returns DOM reference to the DataTable's TABLE element. 63800 * 63801 * @method getTableEl 63802 * @return {HTMLElement} Reference to TABLE element. 63803 */ 63804getTableEl : function() { 63805 return this._elTable; 63806}, 63807 63808/** 63809 * Returns DOM reference to the DataTable's THEAD element. 63810 * 63811 * @method getTheadEl 63812 * @return {HTMLElement} Reference to THEAD element. 63813 */ 63814getTheadEl : function() { 63815 return this._elThead; 63816}, 63817 63818/** 63819 * Returns DOM reference to the DataTable's primary TBODY element. 63820 * 63821 * @method getTbodyEl 63822 * @return {HTMLElement} Reference to TBODY element. 63823 */ 63824getTbodyEl : function() { 63825 return this._elTbody; 63826}, 63827 63828/** 63829 * Returns DOM reference to the DataTable's secondary TBODY element that is 63830 * used to display messages. 63831 * 63832 * @method getMsgTbodyEl 63833 * @return {HTMLElement} Reference to TBODY element. 63834 */ 63835getMsgTbodyEl : function() { 63836 return this._elMsgTbody; 63837}, 63838 63839/** 63840 * Returns DOM reference to the TD element within the secondary TBODY that is 63841 * used to display messages. 63842 * 63843 * @method getMsgTdEl 63844 * @return {HTMLElement} Reference to TD element. 63845 */ 63846getMsgTdEl : function() { 63847 return this._elMsgTd; 63848}, 63849 63850/** 63851 * Returns the corresponding TR reference for a given DOM element, ID string or 63852 * page row index. If the given identifier is a child of a TR element, 63853 * then DOM tree is traversed until a parent TR element is returned, otherwise 63854 * null. Returns null if the row is not considered a primary row (i.e., row 63855 * extensions). 63856 * 63857 * @method getTrEl 63858 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to 63859 * get: by element reference, ID string, page row index, or Record. 63860 * @return {HTMLElement} Reference to TR element, or null. 63861 */ 63862getTrEl : function(row) { 63863 // By Record 63864 if(row instanceof YAHOO.widget.Record) { 63865 return document.getElementById(row.getId()); 63866 } 63867 // By page row index 63868 else if(lang.isNumber(row)) { 63869 var dataRows = Dom.getElementsByClassName(DT.CLASS_REC, "tr", this._elTbody); 63870 return dataRows && dataRows[row] ? dataRows[row] : null; 63871 } 63872 // By ID string or element reference 63873 else if(row) { 63874 var elRow = (lang.isString(row)) ? document.getElementById(row) : row; 63875 63876 // Validate HTML element 63877 if(elRow && elRow.ownerDocument == document) { 63878 // Validate TR element 63879 if(elRow.nodeName.toLowerCase() != "tr") { 63880 // Traverse up the DOM to find the corresponding TR element 63881 elRow = Dom.getAncestorByTagName(elRow,"tr"); 63882 } 63883 63884 return elRow; 63885 } 63886 } 63887 63888 return null; 63889}, 63890 63891/** 63892 * Returns DOM reference to the first primary TR element in the DataTable page, or null. 63893 * 63894 * @method getFirstTrEl 63895 * @return {HTMLElement} Reference to TR element. 63896 */ 63897getFirstTrEl : function() { 63898 var allRows = this._elTbody.rows, 63899 i=0; 63900 while(allRows[i]) { 63901 if(this.getRecord(allRows[i])) { 63902 return allRows[i]; 63903 } 63904 i++; 63905 } 63906 return null; 63907 63908}, 63909 63910/** 63911 * Returns DOM reference to the last primary TR element in the DataTable page, or null. 63912 * 63913 * @method getLastTrEl 63914 * @return {HTMLElement} Reference to last TR element. 63915 */ 63916getLastTrEl : function() { 63917 var allRows = this._elTbody.rows, 63918 i=allRows.length-1; 63919 while(i>-1) { 63920 if(this.getRecord(allRows[i])) { 63921 return allRows[i]; 63922 } 63923 i--; 63924 } 63925 return null; 63926}, 63927 63928/** 63929 * Returns DOM reference to the next TR element from the given primary TR element, or null. 63930 * 63931 * @method getNextTrEl 63932 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element 63933 * reference, ID string, page row index, or Record from which to get next TR element. 63934 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
63935 * that correspond to Records. Non-primary rows (such as row expansions) 63936 * will be skipped. 63937 * @return {HTMLElement} Reference to next TR element. 63938 */ 63939getNextTrEl : function(row, forcePrimary) { 63940 var nThisTrIndex = this.getTrIndex(row); 63941 if(nThisTrIndex !== null) { 63942 var allRows = this._elTbody.rows; 63943 if(forcePrimary) { 63944 while(nThisTrIndex < allRows.length-1) { 63945 row = allRows[nThisTrIndex+1]; 63946 if(this.getRecord(row)) { 63947 return row; 63948 } 63949 nThisTrIndex++; 63950 } 63951 } 63952 else { 63953 if(nThisTrIndex < allRows.length-1) { 63954 return allRows[nThisTrIndex+1]; 63955 } 63956 } 63957 } 63958 63959 YAHOO.log("Could not get next TR element for row " + row, "info", this.toString()); 63960 return null; 63961}, 63962 63963/** 63964 * Returns DOM reference to the previous TR element from the given primary TR element, or null. 63965 * 63966 * @method getPreviousTrEl 63967 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element 63968 * reference, ID string, page row index, or Record from which to get previous TR element. 63969 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements 63970 * from rothat correspond to Records. Non-primary rows (such as row expansions) 63971 * will be skipped. 63972 * @return {HTMLElement} Reference to previous TR element. 63973 */ 63974getPreviousTrEl : function(row, forcePrimary) { 63975 var nThisTrIndex = this.getTrIndex(row); 63976 if(nThisTrIndex !== null) { 63977 var allRows = this._elTbody.rows; 63978 63979 if(forcePrimary) { 63980 while(nThisTrIndex > 0) { 63981 row = allRows[nThisTrIndex-1]; 63982 if(this.getRecord(row)) { 63983 return row; 63984 } 63985 nThisTrIndex--; 63986 } 63987 } 63988 else { 63989 if(nThisTrIndex > 0) { 63990 return allRows[nThisTrIndex-1]; 63991 } 63992 } 63993 } 63994 63995 YAHOO.log("Could not get previous TR element for row " + row, "info", this.toString()); 63996 return null; 63997}, 63998 63999 64000/** 64001 * Workaround for IE bug where hidden or not-in-dom elements cause cellIndex 64002 * value to be incorrect. 64003 * 64004 * @method getCellIndex 64005 * @param cell {HTMLElement | Object} TD element or child of a TD element, or 64006 * object literal of syntax {record:oRecord, column:oColumn}. 64007 * @return {Number} TD.cellIndex value. 64008 */ 64009getCellIndex : function(cell) { 64010 cell = this.getTdEl(cell); 64011 if(cell) { 64012 if(ua.ie > 0) { 64013 var i=0, 64014 tr = cell.parentNode, 64015 allCells = tr.childNodes, 64016 len = allCells.length; 64017 for(; i<len; i++) { 64018 if(allCells[i] == cell) { 64019 return i; 64020 } 64021 } 64022 } 64023 else { 64024 return cell.cellIndex; 64025 } 64026 } 64027}, 64028 64029/** 64030 * Returns DOM reference to a TD liner element. 64031 * 64032 * @method getTdLinerEl 64033 * @param cell {HTMLElement | Object} TD element or child of a TD element, or 64034 * object literal of syntax {record:oRecord, column:oColumn}. 64035 * @return {HTMLElement} Reference to TD liner element. 64036 */ 64037getTdLinerEl : function(cell) { 64038 var elCell = this.getTdEl(cell); 64039 return elCell.firstChild || null; 64040}, 64041 64042/** 64043 * Returns DOM reference to a TD element. Returns null if the row is not 64044 * considered a primary row (i.e., row extensions). 64045 * 64046 * @method getTdEl 64047 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or 64048 * object literal of syntax {record:oRecord, column:oColumn}. 64049 * @return {HTMLElement} Reference to TD element. 64050 */ 64051getTdEl : function(cell) { 64052 var elCell; 64053 var el = Dom.get(cell); 64054 64055 // Validate HTML element 64056 if(el && (el.ownerDocument == document)) { 64057 // Validate TD element 64058 if(el.nodeName.toLowerCase() != "td") { 64059 // Traverse up the DOM to find the corresponding TR element 64060 elCell = Dom.getAncestorByTagName(el, "td"); 64061 } 64062 else { 64063 elCell = el; 64064 } 64065 64066 // Make sure the TD is in this TBODY or is not in DOM 64067 // Bug 2527707 and bug 2263558 64068 if(elCell && ((elCell.parentNode.parentNode == this._elTbody) || 64069 (elCell.parentNode.parentNode === null) || 64070 (elCell.parentNode.parentNode.nodeType === 11))) { 64071 // Now we can return the TD element 64072 return elCell; 64073 } 64074 } 64075 else if(cell) { 64076 var oRecord, nColKeyIndex; 64077 64078 if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) { 64079 oRecord = this.getRecord(cell.recordId); 64080 var oColumn = this.getColumn(cell.columnKey); 64081 if(oColumn) { 64082 nColKeyIndex = oColumn.getKeyIndex(); 64083 } 64084 64085 } 64086 if(cell.record && cell.column && cell.column.getKeyIndex) { 64087 oRecord = cell.record; 64088 nColKeyIndex = cell.column.getKeyIndex(); 64089 } 64090 var elRow = this.getTrEl(oRecord); 64091 if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) { 64092 return elRow.cells[nColKeyIndex] || null; 64093 } 64094 } 64095 64096 return null; 64097}, 64098 64099/** 64100 * Returns DOM reference to the first primary TD element in the DataTable page (by default), 64101 * the first TD element of the optionally given row, or null. 64102 * 64103 * @method getFirstTdEl 64104 * @param row {HTMLElement} (optional) row from which to get first TD 64105 * @return {HTMLElement} Reference to TD element. 64106 */ 64107getFirstTdEl : function(row) { 64108 var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getFirstTrEl(); 64109 if(elRow) { 64110 if(elRow.cells && elRow.cells.length > 0) { 64111 return elRow.cells[0]; 64112 } 64113 else if(elRow.childNodes && elRow.childNodes.length > 0) { 64114 return elRow.childNodes[0]; 64115 } 64116 } 64117 YAHOO.log("Could not get first TD element for row " + elRow, "info", this.toString()); 64118 return null; 64119}, 64120 64121/** 64122 * Returns DOM reference to the last primary TD element in the DataTable page (by default), 64123 * the first TD element of the optionally given row, or null. 64124 * 64125 * @method getLastTdEl 64126 * @param row {HTMLElement} (optional) row from which to get first TD 64127 * @return {HTMLElement} Reference to last TD element. 64128 */ 64129getLastTdEl : function(row) { 64130 var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getLastTrEl(); 64131 if(elRow) { 64132 if(elRow.cells && elRow.cells.length > 0) { 64133 return elRow.cells[elRow.cells.length-1]; 64134 } 64135 else if(elRow.childNodes && elRow.childNodes.length > 0) { 64136 return elRow.childNodes[elRow.childNodes.length-1]; 64137 } 64138 } 64139 YAHOO.log("Could not get last TD element for row " + elRow, "info", this.toString()); 64140 return null; 64141}, 64142 64143/** 64144 * Returns DOM reference to the next TD element from the given cell, or null. 64145 * 64146 * @method getNextTdEl 64147 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or 64148 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element. 64149 * @return {HTMLElement} Reference to next TD element, or null. 64150 */ 64151getNextTdEl : function(cell) { 64152 var elCell = this.getTdEl(cell); 64153 if(elCell) { 64154 var nThisTdIndex = this.getCellIndex(elCell); 64155 var elRow = this.getTrEl(elCell); 64156 if(elRow.cells && (elRow.cells.length) > 0 && (nThisTdIndex < elRow.cells.length-1)) { 64157 return elRow.cells[nThisTdIndex+1]; 64158 } 64159 else if(elRow.childNodes && (elRow.childNodes.length) > 0 && (nThisTdIndex < elRow.childNodes.length-1)) { 64160 return elRow.childNodes[nThisTdIndex+1]; 64161 } 64162 else { 64163 var elNextRow = this.getNextTrEl(elRow); 64164 if(elNextRow) { 64165 return elNextRow.cells[0]; 64166 } 64167 } 64168 } 64169 YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString()); 64170 return null; 64171}, 64172 64173/** 64174 * Returns DOM reference to the previous TD element from the given cell, or null. 64175 * 64176 * @method getPreviousTdEl 64177 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or 64178 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element. 64179 * @return {HTMLElement} Reference to previous TD element, or null. 64180 */ 64181getPreviousTdEl : function(cell) { 64182 var elCell = this.getTdEl(cell); 64183 if(elCell) { 64184 var nThisTdIndex = this.getCellIndex(elCell); 64185 var elRow = this.getTrEl(elCell); 64186 if(nThisTdIndex > 0) { 64187 if(elRow.cells && elRow.cells.length > 0) { 64188 return elRow.cells[nThisTdIndex-1]; 64189 } 64190 else if(elRow.childNodes && elRow.childNodes.length > 0) { 64191 return elRow.childNodes[nThisTdIndex-1]; 64192 } 64193 } 64194 else { 64195 var elPreviousRow = this.getPreviousTrEl(elRow); 64196 if(elPreviousRow) { 64197 return this.getLastTdEl(elPreviousRow); 64198 } 64199 } 64200 } 64201 YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString()); 64202 return null; 64203}, 64204 64205/** 64206 * Returns DOM reference to the above TD element from the given cell, or null. 64207 * 64208 * @method getAboveTdEl 64209 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or 64210 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element. 64211 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
64212 * from rows that correspond to Records. Non-primary rows (such as row expansions) 64213 * will be skipped. 64214 * @return {HTMLElement} Reference to above TD element, or null. 64215 */ 64216getAboveTdEl : function(cell, forcePrimary) { 64217 var elCell = this.getTdEl(cell); 64218 if(elCell) { 64219 var elPreviousRow = this.getPreviousTrEl(elCell, forcePrimary); 64220 if(elPreviousRow ) { 64221 var cellIndex = this.getCellIndex(elCell); 64222 if(elPreviousRow.cells && elPreviousRow.cells.length > 0) { 64223 return elPreviousRow.cells[cellIndex] ? elPreviousRow.cells[cellIndex] : null; 64224 } 64225 else if(elPreviousRow.childNodes && elPreviousRow.childNodes.length > 0) { 64226 return elPreviousRow.childNodes[cellIndex] ? elPreviousRow.childNodes[cellIndex] : null; 64227 } 64228 } 64229 } 64230 YAHOO.log("Could not get above TD element for cell " + cell, "info", this.toString()); 64231 return null; 64232}, 64233 64234/** 64235 * Returns DOM reference to the below TD element from the given cell, or null. 64236 * 64237 * @method getBelowTdEl 64238 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or 64239 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element. 64240 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements 64241 * from rows that correspond to Records. Non-primary rows (such as row expansions) 64242 * will be skipped. 64243 * @return {HTMLElement} Reference to below TD element, or null. 64244 */ 64245getBelowTdEl : function(cell, forcePrimary) { 64246 var elCell = this.getTdEl(cell); 64247 if(elCell) { 64248 var elNextRow = this.getNextTrEl(elCell, forcePrimary); 64249 if(elNextRow) { 64250 var cellIndex = this.getCellIndex(elCell); 64251 if(elNextRow.cells && elNextRow.cells.length > 0) { 64252 return elNextRow.cells[cellIndex] ? elNextRow.cells[cellIndex] : null; 64253 } 64254 else if(elNextRow.childNodes && elNextRow.childNodes.length > 0) { 64255 return elNextRow.childNodes[cellIndex] ? elNextRow.childNodes[cellIndex] : null; 64256 } 64257 } 64258 } 64259 YAHOO.log("Could not get below TD element for cell " + cell, "info", this.toString()); 64260 return null; 64261}, 64262 64263/** 64264 * Returns DOM reference to a TH liner element. Needed to normalize for resizeable 64265 * Columns, which have an additional resizer liner DIV element between the TH 64266 * element and the liner DIV element. 64267 * 64268 * @method getThLinerEl 64269 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance, 64270 * DOM element reference, or string ID. 64271 * @return {HTMLElement} Reference to TH liner element. 64272 */ 64273getThLinerEl : function(theadCell) { 64274 var oColumn = this.getColumn(theadCell); 64275 return (oColumn) ? oColumn.getThLinerEl() : null; 64276}, 64277 64278/** 64279 * Returns DOM reference to a TH element. 64280 * 64281 * @method getThEl 64282 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance, 64283 * DOM element reference, or string ID. 64284 * @return {HTMLElement} Reference to TH element. 64285 */ 64286getThEl : function(theadCell) { 64287 var elTh; 64288 64289 // Validate Column instance 64290 if(theadCell instanceof YAHOO.widget.Column) { 64291 var oColumn = theadCell; 64292 elTh = oColumn.getThEl(); 64293 if(elTh) { 64294 return elTh; 64295 } 64296 } 64297 // Validate HTML element 64298 else { 64299 var el = Dom.get(theadCell); 64300 64301 if(el && (el.ownerDocument == document)) { 64302 // Validate TH element 64303 if(el.nodeName.toLowerCase() != "th") { 64304 // Traverse up the DOM to find the corresponding TR element 64305 elTh = Dom.getAncestorByTagName(el,"th"); 64306 } 64307 else { 64308 elTh = el; 64309 } 64310 64311 return elTh; 64312 } 64313 } 64314 64315 return null; 64316}, 64317 64318/** 64319 * Returns the page row index of given primary row. Returns null if the row is not on the 64320 * current DataTable page, or if row is not considered a primary row (i.e., row 64321 * extensions). 64322 * 64323 * @method getTrIndex 64324 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID 64325 * string reference to an element within the DataTable page, a Record instance, 64326 * or a Record's RecordSet index. 64327 * @return {Number} Page row index, or null if data row does not exist or is not on current page. 64328 */ 64329getTrIndex : function(row) { 64330 var record = this.getRecord(row), 64331 index = this.getRecordIndex(record), 64332 tr; 64333 if(record) { 64334 tr = this.getTrEl(record); 64335 if(tr) { 64336 return tr.sectionRowIndex; 64337 } 64338 else { 64339 var oPaginator = this.get("paginator"); 64340 if(oPaginator) { 64341 return oPaginator.get('recordOffset') + index; 64342 } 64343 else { 64344 return index; 64345 } 64346 } 64347 } 64348 YAHOO.log("Could not get page row index for row " + row, "info", this.toString()); 64349 return null; 64350}, 64351 64352 64353 64354 64355 64356 64357 64358 64359 64360 64361 64362 64363 64364 64365 64366 64367 64368 64369 64370 64371 64372 64373 64374 64375 64376 64377 64378 64379 64380 64381 64382 64383 64384 64385 64386 64387 64388 64389 64390 64391 64392 64393 64394 64395 64396 64397// TABLE FUNCTIONS 64398 64399/** 64400 * Loads new data. Convenience method that calls DataSource's sendRequest() 64401 * method under the hood. 64402 * 64403 * @method load 64404 * @param oConfig {object} Optional configuration parameters: 64405 * 64406 * <dl> 64407 * <dt>request</dt><dd>Pass in a new request, or initialRequest is used.</dd> 64408 * <dt>callback</dt><dd>Pass in DataSource sendRequest() callback object, or the following is used: 64409 * <dl> 64410 * <dt>success</dt><dd>datatable.onDataReturnInitializeTable</dd> 64411 * <dt>failure</dt><dd>datatable.onDataReturnInitializeTable</dd> 64412 * <dt>scope</dt><dd>datatable</dd> 64413 * <dt>argument</dt><dd>datatable.getState()</dd> 64414 * </dl> 64415 * </dd> 64416 * <dt>datasource</dt><dd>Pass in a new DataSource instance to override the current DataSource for this transaction.</dd> 64417 * </dl> 64418 */ 64419load : function(oConfig) { 64420 oConfig = oConfig || {}; 64421 64422 (oConfig.datasource || this._oDataSource).sendRequest(oConfig.request || this.get("initialRequest"), oConfig.callback || { 64423 success: this.onDataReturnInitializeTable, 64424 failure: this.onDataReturnInitializeTable, 64425 scope: this,
64426 argument: this.getState() 64427 }); 64428}, 64429 64430/** 64431 * Resets a RecordSet with the given data and populates the page view 64432 * with the new data. Any previous data, and selection and sort states are 64433 * cleared. New data should be added as a separate step. 64434 * 64435 * @method initializeTable 64436 */ 64437initializeTable : function() { 64438 // Reset init flag 64439 this._bInit = true; 64440 64441 // Clear the RecordSet 64442 this._oRecordSet.reset(); 64443 64444 // Clear the Paginator's totalRecords if paginating 64445 var pag = this.get('paginator'); 64446 if (pag) { 64447 pag.set('totalRecords',0); 64448 } 64449 64450 // Clear selections 64451 this._unselectAllTrEls(); 64452 this._unselectAllTdEls(); 64453 this._aSelections = null; 64454 this._oAnchorRecord = null; 64455 this._oAnchorCell = null; 64456 64457 // Clear sort 64458 this.set("sortedBy", null); 64459}, 64460 64461/** 64462 * Internal wrapper calls run() on render Chain instance. 64463 * 64464 * @method _runRenderChain 64465 * @private 64466 */ 64467_runRenderChain : function() { 64468 this._oChainRender.run(); 64469}, 64470 64471/** 64472 * Returns array of Records for current view. For example, if paginated, it 64473 * returns the subset of Records for current page. 64474 * 64475 * @method _getViewRecords 64476 * @protected 64477 * @return {Array} Array of Records to display in current view. 64478 */ 64479_getViewRecords : function() { 64480 // Paginator is enabled, show a subset of Records 64481 var oPaginator = this.get('paginator'); 64482 if(oPaginator) { 64483 return this._oRecordSet.getRecords( 64484 oPaginator.getStartIndex(), 64485 oPaginator.getRowsPerPage()); 64486 } 64487 // Not paginated, show all records 64488 else { 64489 return this._oRecordSet.getRecords(); 64490 } 64491 64492}, 64493 64494/** 64495 * Renders the view with existing Records from the RecordSet while 64496 * maintaining sort, pagination, and selection states. For performance, reuses 64497 * existing DOM elements when possible while deleting extraneous elements. 64498 * 64499 * @method render 64500 */ 64501render : function() { 64502//YAHOO.example.Performance.trialStart = new Date(); 64503 64504 this._oChainRender.stop(); 64505 64506 this.fireEvent("beforeRenderEvent"); 64507 YAHOO.log("DataTable rendering...", "info", this.toString()); 64508 64509 var i, j, k, len, 64510 allRecords = this._getViewRecords(); 64511 64512 64513 // From the top, update in-place existing rows, so as to reuse DOM elements 64514 var elTbody = this._elTbody, 64515 loopN = this.get("renderLoopSize"), 64516 nRecordsLength = allRecords.length; 64517 64518 // Table has rows 64519 if(nRecordsLength > 0) { 64520 elTbody.style.display = "none"; 64521 while(elTbody.lastChild) { 64522 elTbody.removeChild(elTbody.lastChild); 64523 } 64524 elTbody.style.display = ""; 64525 64526 // Set up the loop Chain to render rows 64527 this._oChainRender.add({ 64528 method: function(oArg) { 64529 if((this instanceof DT) && this._sId) { 64530 var i = oArg.nCurrentRecord, 64531 endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ? 64532 nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength), 64533 elRow, nextSibling; 64534 64535 elTbody.style.display = "none"; 64536 64537 for(; i<endRecordIndex; i++) { 64538 elRow = Dom.get(allRecords[i].getId()); 64539 elRow = elRow || this._addTrEl(allRecords[i]); 64540 nextSibling = elTbody.childNodes[i] || null; 64541 elTbody.insertBefore(elRow, nextSibling); 64542 } 64543 elTbody.style.display = ""; 64544 64545 // Set up for the next loop 64546 oArg.nCurrentRecord = i; 64547 } 64548 }, 64549 scope: this, 64550 iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1, 64551 argument: { 64552 nCurrentRecord: 0,//nRecordsLength-1, // Start at first Record 64553 nLoopLength: (loopN > 0) ? loopN : nRecordsLength 64554 }, 64555 timeout: (loopN > 0) ? 0 : -1 64556 }); 64557 64558 // Post-render tasks 64559 this._oChainRender.add({ 64560 method: function(oArg) { 64561 if((this instanceof DT) && this._sId) { 64562 while(elTbody.rows.length > nRecordsLength) { 64563 elTbody.removeChild(elTbody.lastChild); 64564 } 64565 this._setFirstRow(); 64566 this._setLastRow(); 64567 this._setRowStripes(); 64568 this._setSelections(); 64569 } 64570 }, 64571 scope: this, 64572 timeout: (loopN > 0) ? 0 : -1 64573 }); 64574 64575 } 64576 // Table has no rows 64577 else { 64578 // Set up the loop Chain to delete rows 64579 var nTotal = elTbody.rows.length; 64580 if(nTotal > 0) { 64581 this._oChainRender.add({ 64582 method: function(oArg) { 64583 if((this instanceof DT) && this._sId) { 64584 var i = oArg.nCurrent, 64585 loopN = oArg.nLoopLength,
64586 nIterEnd = (i - loopN < 0) ? 0 : i - loopN; 64587 64588 elTbody.style.display = "none"; 64589 64590 for(; i>nIterEnd; i--) { 64591 elTbody.deleteRow(-1); 64592 } 64593 elTbody.style.display = ""; 64594 64595 // Set up for the next loop 64596 oArg.nCurrent = i; 64597 } 64598 }, 64599 scope: this, 64600 iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1, 64601 argument: { 64602 nCurrent: nTotal, 64603 nLoopLength: (loopN > 0) ? loopN : nTotal 64604 }, 64605 timeout: (loopN > 0) ? 0 : -1 64606 }); 64607 } 64608 } 64609 this._runRenderChain(); 64610}, 64611 64612/** 64613 * Disables DataTable UI. 64614 * 64615 * @method disable 64616 */ 64617disable : function() { 64618 this._disabled = true; 64619 var elTable = this._elTable; 64620 var elMask = this._elMask; 64621 elMask.style.width = elTable.offsetWidth + "px"; 64622 elMask.style.height = elTable.offsetHeight + "px"; 64623 elMask.style.left = elTable.offsetLeft + "px"; 64624 elMask.style.display = ""; 64625 this.fireEvent("disableEvent"); 64626}, 64627 64628/** 64629 * Undisables DataTable UI. 64630 * 64631 * @method undisable 64632 */ 64633undisable : function() { 64634 this._disabled = false; 64635 this._elMask.style.display = "none"; 64636 this.fireEvent("undisableEvent"); 64637}, 64638 64639 /** 64640 * Returns disabled state. 64641 * 64642 * @method isDisabled 64643 * @return {Boolean} True if UI is disabled, otherwise false 64644 */ 64645isDisabled : function() { 64646 return this._disabled; 64647}, 64648 64649/** 64650 * Nulls out the entire DataTable instance and related objects, removes attached 64651 * event listeners, and clears out DOM elements inside the container. After 64652 * calling this method, the instance reference should be expliclitly nulled by 64653 * implementer, as in myDataTable = null. Use with caution! 64654 * 64655 * @method destroy 64656 */ 64657destroy : function() { 64658 // Store for later 64659 var instanceName = this.toString(); 64660 64661 this._oChainRender.stop(); 64662 64663 // Destroy ColumnDD and ColumnResizers 64664 this._destroyColumnHelpers(); 64665 64666 // Destroy all CellEditors 64667 var oCellEditor; 64668 for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) { 64669 oCellEditor = this._oColumnSet.flat[i].editor; 64670 if(oCellEditor && oCellEditor.destroy) { 64671 oCellEditor.destroy(); 64672 this._oColumnSet.flat[i].editor = null; 64673 } 64674 } 64675 64676 // Destroy Paginator 64677 this._destroyPaginator(); 64678 64679 // Unhook custom events 64680 this._oRecordSet.unsubscribeAll(); 64681 this.unsubscribeAll(); 64682 64683 // Unhook DOM events 64684 Ev.removeListener(document, "click", this._onDocumentClick); 64685 64686 // Clear out the container 64687 this._destroyContainerEl(this._elContainer); 64688 64689 // Null out objects 64690 for(var param in this) { 64691 if(lang.hasOwnProperty(this, param)) { 64692 this[param] = null; 64693 } 64694 } 64695 64696 // Clean up static values 64697 DT._nCurrentCount--; 64698 64699 if(DT._nCurrentCount < 1) { 64700 if(DT._elDynStyleNode) { 64701 document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode); 64702 DT._elDynStyleNode = null; 64703 } 64704 } 64705 64706 YAHOO.log("DataTable instance destroyed: " + instanceName); 64707}, 64708 64709/** 64710 * Displays message within secondary TBODY. 64711 * 64712 * @method showTableMessage 64713 * @param sHTML {HTML} (optional) Value for innerHTML. 64714 * @param sClassName {String} (optional) Classname. 64715 */ 64716showTableMessage : function(sHTML, sClassName) { 64717 var elCell = this._elMsgTd; 64718 if(lang.isString(sHTML)) { 64719 elCell.firstChild.innerHTML = sHTML; 64720 } 64721 if(lang.isString(sClassName)) { 64722 elCell.className = sClassName; 64723 } 64724 64725 this._elMsgTbody.style.display = ""; 64726 64727 this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName}); 64728 YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString()); 64729}, 64730 64731/** 64732 * Hides secondary TBODY. 64733 * 64734 * @method hideTableMessage 64735 */ 64736hideTableMessage : function() { 64737 if(this._elMsgTbody.style.display != "none") { 64738 this._elMsgTbody.style.display = "none"; 64739 this._elMsgTbody.parentNode.style.width = ""; 64740 this.fireEvent("tableMsgHideEvent"); 64741 YAHOO.log("DataTable message hidden", "info", this.toString()); 64742 } 64743}, 64744 64745/** 64746 * Brings focus to the TBODY element. Alias to focusTbodyEl. 64747 *
64748 * @method focus 64749 */ 64750focus : function() { 64751 this.focusTbodyEl(); 64752}, 64753 64754/** 64755 * Brings focus to the THEAD element. 64756 * 64757 * @method focusTheadEl 64758 */ 64759focusTheadEl : function() { 64760 this._focusEl(this._elThead); 64761}, 64762 64763/** 64764 * Brings focus to the TBODY element. 64765 * 64766 * @method focusTbodyEl 64767 */ 64768focusTbodyEl : function() { 64769 this._focusEl(this._elTbody); 64770}, 64771 64772/** 64773 * Setting display:none on DataTable or any parent may impact width validations. 64774 * After setting display back to "", implementers should call this method to 64775 * manually perform those validations. 64776 * 64777 * @method onShow 64778 */ 64779onShow : function() { 64780 this.validateColumnWidths(); 64781 64782 for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) { 64783 col = allKeys[i]; 64784 if(col._ddResizer) { 64785 col._ddResizer.resetResizerEl(); 64786 } 64787 } 64788}, 64789 64790 64791 64792 64793 64794 64795 64796 64797 64798 64799 64800 64801 64802 64803 64804 64805 64806 64807 64808 64809 64810 64811 64812 64813 64814 64815 64816 64817 64818 64819 64820 64821 64822 64823 64824 64825 64826 64827 64828 64829 64830 64831 64832 64833 64834 64835 64836 64837 64838 64839 64840 64841 64842 64843 64844 64845 64846 64847 64848 64849 64850 64851 64852 64853 64854 64855 64856// RECORDSET FUNCTIONS 64857 64858/** 64859 * Returns Record index for given TR element or page row index. 64860 * 64861 * @method getRecordIndex 64862 * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR 64863 * element reference or page row index. 64864 * @return {Number} Record's RecordSet index, or null. 64865 */ 64866getRecordIndex : function(row) { 64867 var nTrIndex; 64868 64869 if(!lang.isNumber(row)) { 64870 // By Record 64871 if(row instanceof YAHOO.widget.Record) { 64872 return this._oRecordSet.getRecordIndex(row); 64873 } 64874 // By element reference 64875 else { 64876 // Find the TR element 64877 var el = this.getTrEl(row); 64878 if(el) { 64879 nTrIndex = el.sectionRowIndex; 64880 } 64881 } 64882 } 64883 // By page row index 64884 else { 64885 nTrIndex = row; 64886 } 64887 64888 if(lang.isNumber(nTrIndex)) { 64889 var oPaginator = this.get("paginator"); 64890 if(oPaginator) { 64891 return oPaginator.get('recordOffset') + nTrIndex; 64892 } 64893 else { 64894 return nTrIndex; 64895 } 64896 } 64897 64898 YAHOO.log("Could not get Record index for row " + row, "info", this.toString()); 64899 return null; 64900}, 64901 64902/** 64903 * For the given identifier, returns the associated Record instance. 64904 * 64905 * @method getRecord 64906 * @param row {HTMLElement | Number | String} DOM reference to a TR element (or 64907 * child of a TR element), RecordSet position index, or Record ID. 64908 * @return {YAHOO.widget.Record} Record instance. 64909 */ 64910getRecord : function(row) { 64911 var oRecord = this._oRecordSet.getRecord(row); 64912 64913 if(!oRecord) { 64914 // Validate TR element 64915 var elRow = this.getTrEl(row); 64916 if(elRow) { 64917 oRecord = this._oRecordSet.getRecord(elRow.id); 64918 } 64919 } 64920 64921 if(oRecord instanceof YAHOO.widget.Record) { 64922 return this._oRecordSet.getRecord(oRecord); 64923 } 64924 else { 64925 YAHOO.log("Could not get Record for row at " + row, "info", this.toString()); 64926 return null; 64927 } 64928}, 64929 64930 64931 64932 64933 64934 64935 64936 64937 64938 64939 64940 64941 64942 64943 64944 64945 64946 64947 64948 64949 64950 64951 64952 64953 64954 64955 64956 64957 64958 64959 64960 64961 64962 64963 64964 64965 64966 64967 64968 64969 64970 64971 64972 64973 64974 64975// COLUMN FUNCTIONS 64976 64977/** 64978 * For the given identifier, returns the associated Column instance. Note: For 64979 * getting Columns by Column ID string, please use the method getColumnById(). 64980 * 64981 * @method getColumn 64982 * @param column {HTMLElement | String | Number} TH/TD element (or child of a 64983 * TH/TD element), a Column key, or a ColumnSet key index. 64984 * @return {YAHOO.widget.Column} Column instance. 64985 */ 64986getColumn : function(column) { 64987 var oColumn = this._oColumnSet.getColumn(column); 64988 64989 if(!oColumn) { 64990 // Validate TD element 64991 var elCell = this.getTdEl(column); 64992 if(elCell) { 64993 oColumn = this._oColumnSet.getColumn(this.getCellIndex(elCell)); 64994 } 64995 // Validate TH element 64996 else { 64997 elCell = this.getThEl(column); 64998 if(elCell) { 64999 // Find by TH el ID 65000 var allColumns = this._oColumnSet.flat; 65001 for(var i=0, len=allColumns.length; i<len; i++) { 65002 if(allColumns[i].getThEl().id === elCell.id) { 65003 oColumn = allColumns[i]; 65004 } 65005 } 65006 } 65007 } 65008 } 65009 if(!oColumn) { 65010 YAHOO.log("Could not get Column for column at " + column, "info", this.toString()); 65011 } 65012 return oColumn; 65013}, 65014 65015/** 65016 * For the given Column ID, returns the associated Column instance. Note: For 65017 * getting Columns by key, please use the method getColumn(). 65018 * 65019 * @method getColumnById 65020 * @param column {String} Column ID string. 65021 * @return {YAHOO.widget.Column} Column instance. 65022 */ 65023getColumnById : function(column) { 65024 return this._oColumnSet.getColumnById(column); 65025}, 65026 65027/** 65028 * For the given Column instance, returns next direction to sort. 65029 * 65030 * @method getColumnSortDir 65031 * @param oColumn {YAHOO.widget.Column} Column instance. 65032 * @param oSortedBy {Object} (optional) Specify the state, or use current state. 65033 * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC. 65034 */ 65035getColumnSortDir : function(oColumn, oSortedBy) { 65036 // Backward compatibility 65037 if(oColumn.sortOptions && oColumn.sortOptions.defaultDir) { 65038 if(oColumn.sortOptions.defaultDir == "asc") { 65039 oColumn.sortOptions.defaultDir = DT.CLASS_ASC; 65040 } 65041 else if (oColumn.sortOptions.defaultDir == "desc") { 65042 oColumn.sortOptions.defaultDir = DT.CLASS_DESC; 65043 } 65044 } 65045 65046 // What is the Column's default sort direction? 65047 var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC; 65048 65049 // Is the Column currently sorted? 65050 var bSorted = false;
65051 oSortedBy = oSortedBy || this.get("sortedBy"); 65052 if(oSortedBy && (oSortedBy.key === oColumn.key)) { 65053 bSorted = true; 65054 if(oSortedBy.dir) { 65055 sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC; 65056 } 65057 else { 65058 sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC; 65059 } 65060 } 65061 return sortDir; 65062}, 65063 65064/** 65065 * Overridable method gives implementers a hook to show loading message before 65066 * sorting Column. 65067 * 65068 * @method doBeforeSortColumn 65069 * @param oColumn {YAHOO.widget.Column} Column instance. 65070 * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or 65071 * YAHOO.widget.DataTable.CLASS_DESC. 65072 * @return {Boolean} Return true to continue sorting Column. 65073 */ 65074doBeforeSortColumn : function(oColumn, sSortDir) { 65075 this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING); 65076 return true; 65077}, 65078 65079/** 65080 * Sorts given Column. If "dynamicData" is true, current selections are purged before 65081 * a request is sent to the DataSource for data for the new state (using the 65082 * request returned by "generateRequest()"). 65083 * 65084 * @method sortColumn 65085 * @param oColumn {YAHOO.widget.Column} Column instance. 65086 * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or 65087 * YAHOO.widget.DataTable.CLASS_DESC 65088 */ 65089sortColumn : function(oColumn, sDir) { 65090 if(oColumn && (oColumn instanceof YAHOO.widget.Column)) { 65091 if(!oColumn.sortable) { 65092 Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE); 65093 } 65094 65095 // Validate given direction 65096 if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) { 65097 sDir = null; 65098 } 65099 65100 // Get the sort dir 65101 var sSortDir = sDir || this.getColumnSortDir(oColumn); 65102 65103 // Is the Column currently sorted? 65104 var oSortedBy = this.get("sortedBy") || {}; 65105 var bSorted = (oSortedBy.key === oColumn.key) ? true : false; 65106 65107 var ok = this.doBeforeSortColumn(oColumn, sSortDir); 65108 if(ok) { 65109 // Server-side sort 65110 if(this.get("dynamicData")) { 65111 // Get current state 65112 var oState = this.getState(); 65113 65114 // Reset record offset, if paginated 65115 if(oState.pagination) { 65116 oState.pagination.recordOffset = 0; 65117 } 65118 65119 // Update sortedBy to new values 65120 oState.sortedBy = { 65121 key: oColumn.key, 65122 dir: sSortDir 65123 }; 65124 65125 // Get the request for the new state 65126 var request = this.get("generateRequest")(oState, this); 65127 65128 // Purge selections 65129 this.unselectAllRows(); 65130 this.unselectAllCells(); 65131 65132 // Send request for new data 65133 var callback = { 65134 success : this.onDataReturnSetRows, 65135 failure : this.onDataReturnSetRows, 65136 argument : oState, // Pass along the new state to the callback 65137 scope : this 65138 }; 65139 this._oDataSource.sendRequest(request, callback); 65140 } 65141 // Client-side sort 65142 else { 65143 // Is there a custom sort handler function defined? 65144 var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ? 65145 // Custom sort function 65146 oColumn.sortOptions.sortFunction : null; 65147 65148 // Sort the Records 65149 if(!bSorted || sDir || sortFnc) { 65150 // Default sort function if necessary 65151 sortFnc = sortFnc || this.get("sortFunction"); 65152 // Get the field to sort 65153 var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field; 65154 65155 // Sort the Records 65156 this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false), sField); 65157 } 65158 // Just reverse the Records 65159 else { 65160 this._oRecordSet.reverseRecords(); 65161 } 65162 65163 // Reset to first page if paginated 65164 var oPaginator = this.get('paginator'); 65165 if (oPaginator) { 65166 // Set page silently, so as not to fire change event. 65167 oPaginator.setPage(1,true); 65168 } 65169 65170 // Update UI via sortedBy 65171 this.render(); 65172 this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn}); 65173 } 65174 65175 this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir}); 65176 YAHOO.log("Column \"" + oColumn.key + "\" sorted \"" + sSortDir + "\"", "info", this.toString()); 65177 return; 65178 } 65179 } 65180 YAHOO.log("Could not sort Column \"" + oColumn.key + "\"", "warn", this.toString()); 65181}, 65182 65183/** 65184 * Sets given Column to given pixel width. If new width is less than minimum 65185 * width, sets to minimum width. Updates oColumn.width value. 65186 * 65187 * @method setColumnWidth 65188 * @param oColumn {YAHOO.widget.Column} Column instance. 65189 * @param nWidth {Number} New width in pixels. A null value auto-sizes Column, 65190 * subject to minWidth and maxAutoWidth validations. 65191 */ 65192setColumnWidth : function(oColumn, nWidth) { 65193 if(!(oColumn instanceof YAHOO.widget.Column)) { 65194 oColumn = this.getColumn(oColumn); 65195 } 65196 if(oColumn) { 65197 // Validate new width against minimum width 65198 if(lang.isNumber(nWidth)) { 65199 // This is why we must require a Number... :-| 65200 nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth; 65201 65202 // Save state 65203 oColumn.width = nWidth; 65204 65205 // Resize the DOM elements 65206 this._setColumnWidth(oColumn, nWidth+"px"); 65207 65208 this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth}); 65209 YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString()); 65210 } 65211 // Unsets a width to auto-size 65212 else if(nWidth === null) { 65213 // Save state 65214 oColumn.width = nWidth; 65215 65216 // Resize the DOM elements 65217 this._setColumnWidth(oColumn, "auto"); 65218 this.validateColumnWidths(oColumn); 65219 this.fireEvent("columnUnsetWidthEvent",{column:oColumn}); 65220 YAHOO.log("Column " + oColumn + " width unset", "info", this.toString()); 65221 } 65222 65223 // Bug 2339454: resize then sort misaligment 65224 this._clearTrTemplateEl(); 65225 } 65226 else { 65227 YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString()); 65228 } 65229}, 65230 65231/** 65232 * Sets liner DIV elements of given Column to given width. When value should be 65233 * auto-calculated to fit content overflow is set to visible, otherwise overflow 65234 * is set to hidden. No validations against minimum width and no updating 65235 * Column.width value. 65236 * 65237 * @method _setColumnWidth 65238 * @param oColumn {YAHOO.widget.Column} Column instance. 65239 * @param sWidth {String} New width value. 65240 * @param sOverflow {String} Should be "hidden" when Column width is explicitly 65241 * being set to a value, but should be "visible" when Column is meant to auto-fit content. 65242 * @private 65243 */ 65244_setColumnWidth : function(oColumn, sWidth, sOverflow) { 65245 if(oColumn && (oColumn.getKeyIndex() !== null)) { 65246 sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden'); 65247 65248 // Dynamic style algorithm 65249 if(!DT._bDynStylesFallback) { 65250 this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow); 65251 } 65252 // Dynamic function algorithm 65253 else { 65254 this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow); 65255 } 65256 } 65257 else { 65258 YAHOO.log("Could not set width of unknown Column " + oColumn + " to " + sWidth, "warn", this.toString()); 65259 } 65260}, 65261 65262/** 65263 * Updates width of a Column's liner DIV elements by dynamically creating a 65264 * STYLE node and writing and updating CSS style rules to it. If this fails during 65265 * runtime, the fallback method _setColumnWidthDynFunction() will be called. 65266 * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is 65267 * nested within another TABLE element. For these cases, it is recommended to 65268 * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE. 65269 * 65270 * @method _setColumnWidthDynStyles 65271 * @param oColumn {YAHOO.widget.Column} Column instance. 65272 * @param sWidth {String} New width value. 65273 * @private 65274 */ 65275_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) { 65276 var s = DT._elDynStyleNode, 65277 rule; 65278 65279 // Create a new STYLE node 65280 if(!s) { 65281 s = document.createElement('style'); 65282 s.type = 'text/css'; 65283 s = document.getElementsByTagName('head').item(0).appendChild(s); 65284 DT._elDynStyleNode = s; 65285 } 65286 65287 // We have a STYLE node to update 65288 if(s) { 65289 // Use unique classname for this Column instance as a hook for resizing 65290 var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER; 65291 65292 // Hide for performance 65293 if(this._elTbody) { 65294 this._elTbody.style.display = 'none'; 65295 } 65296 65297 rule = DT._oDynStyles[sClassname]; 65298 65299 // The Column does not yet have a rule 65300 if(!rule) { 65301 if(s.styleSheet && s.styleSheet.addRule) { 65302 s.styleSheet.addRule(sClassname,"overflow:"+sOverflow); 65303 s.styleSheet.addRule(sClassname,'width:'+sWidth); 65304 rule = s.styleSheet.rules[s.styleSheet.rules.length-1]; 65305 DT._oDynStyles[sClassname] = rule; 65306 } 65307 else if(s.sheet && s.sheet.insertRule) { 65308 s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length); 65309 rule = s.sheet.cssRules[s.sheet.cssRules.length-1]; 65310 DT._oDynStyles[sClassname] = rule; 65311 } 65312 } 65313 // We have a rule to update 65314 else { 65315 rule.style.overflow = sOverflow; 65316 rule.style.width = sWidth; 65317 } 65318 65319 // Unhide 65320 if(this._elTbody) { 65321 this._elTbody.style.display = ''; 65322 } 65323 } 65324
65325 // That was not a success, we must call the fallback routine 65326 if(!rule) { 65327 DT._bDynStylesFallback = true; 65328 this._setColumnWidthDynFunction(oColumn, sWidth); 65329 } 65330}, 65331 65332/** 65333 * Updates width of a Column's liner DIV elements by dynamically creating a 65334 * function to update all element style properties in one pass. Note: This 65335 * technique is not supported in sandboxed environments that prohibit EVALs. 65336 * 65337 * @method _setColumnWidthDynFunction 65338 * @param oColumn {YAHOO.widget.Column} Column instance. 65339 * @param sWidth {String} New width value. 65340 * @private 65341 */ 65342_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) { 65343 // TODO: why is this here? 65344 if(sWidth == 'auto') { 65345 sWidth = ''; 65346 } 65347 65348 // Create one function for each value of rows.length 65349 var rowslen = this._elTbody ? this._elTbody.rows.length : 0; 65350 65351 // Dynamically create the function 65352 if (!this._aDynFunctions[rowslen]) { 65353 65354 //Compile a custom function to do all the liner div width 65355 //assignments at the same time. A unique function is required 65356 //for each unique number of rows in _elTbody. This will 65357 //result in a function declaration like: 65358 //function (oColumn,sWidth,sOverflow) { 65359 // var colIdx = oColumn.getKeyIndex(); 65360 // oColumn.getThLinerEl().style.overflow = 65361 // this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow = 65362 // this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow = 65363 // ... (for all row indices in this._elTbody.rows.length - 1) 65364 // this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow = 65365 // sOverflow; 65366 // oColumn.getThLinerEl().style.width = 65367 // this._elTbody.rows[0].cells[colIdx].firstChild.style.width = 65368 // this._elTbody.rows[1].cells[colIdx].firstChild.style.width = 65369 // ... (for all row indices in this._elTbody.rows.length - 1) 65370 // this._elTbody.rows[99].cells[colIdx].firstChild.style.width = 65371 // sWidth; 65372 //} 65373 65374 var i,j,k; 65375 var resizerDef = [ 65376 'var colIdx=oColumn.getKeyIndex();', 65377 'oColumn.getThLinerEl().style.overflow=' 65378 ]; 65379 for (i=rowslen-1, j=2; i >= 0; --i) { 65380 resizerDef[j++] = 'this._elTbody.rows['; 65381 resizerDef[j++] = i; 65382 resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow='; 65383 } 65384 resizerDef[j] = 'sOverflow;'; 65385 resizerDef[j+1] = 'oColumn.getThLinerEl().style.width='; 65386 for (i=rowslen-1, k=j+2; i >= 0; --i) { 65387 resizerDef[k++] = 'this._elTbody.rows['; 65388 resizerDef[k++] = i; 65389 resizerDef[k++] = '].cells[colIdx].firstChild.style.width='; 65390 } 65391 resizerDef[k] = 'sWidth;'; 65392 this._aDynFunctions[rowslen] = 65393 new Function('oColumn','sWidth','sOverflow',resizerDef.join('')); 65394 } 65395 65396 // Get the function to execute 65397 var resizerFn = this._aDynFunctions[rowslen]; 65398 65399 // TODO: Hide TBODY for performance in _setColumnWidthDynFunction? 65400 if (resizerFn) { 65401 resizerFn.call(this,oColumn,sWidth,sOverflow); 65402 } 65403}, 65404 65405/** 65406 * For one or all Columns, when Column is not hidden, width is not set, and minWidth 65407 * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth. 65408 * 65409 * @method validateColumnWidths 65410 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated. 65411 */ 65412validateColumnWidths : function(oColumn) { 65413 var elColgroup = this._elColgroup; 65414 var elColgroupClone = elColgroup.cloneNode(true); 65415 var bNeedsValidation = false; 65416 var allKeys = this._oColumnSet.keys; 65417 var elThLiner; 65418 // Validate just one Column's minWidth and/or maxAutoWidth 65419 if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) { 65420 elThLiner = oColumn.getThLinerEl(); 65421 if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) { 65422 elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width = 65423 oColumn.minWidth + 65424 (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) + 65425 (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px"; 65426 bNeedsValidation = true; 65427 } 65428 else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) { 65429 this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden"); 65430 } 65431 } 65432 // Validate all Columns 65433 else { 65434 for(var i=0, len=allKeys.length; i<len; i++) {
65435 oColumn = allKeys[i]; 65436 if(!oColumn.hidden && !oColumn.width) { 65437 elThLiner = oColumn.getThLinerEl(); 65438 if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) { 65439 elColgroupClone.childNodes[i].style.width = 65440 oColumn.minWidth + 65441 (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) + 65442 (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px"; 65443 bNeedsValidation = true; 65444 } 65445 else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) { 65446 this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden"); 65447 } 65448 } 65449 } 65450 } 65451 if(bNeedsValidation) { 65452 elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup); 65453 this._elColgroup = elColgroupClone; 65454 } 65455}, 65456 65457/** 65458 * Clears minWidth. 65459 * 65460 * @method _clearMinWidth 65461 * @param oColumn {YAHOO.widget.Column} Which Column. 65462 * @private 65463 */ 65464_clearMinWidth : function(oColumn) { 65465 if(oColumn.getKeyIndex() !== null) { 65466 this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = ''; 65467 } 65468}, 65469 65470/** 65471 * Restores minWidth. 65472 * 65473 * @method _restoreMinWidth 65474 * @param oColumn {YAHOO.widget.Column} Which Column. 65475 * @private 65476 */ 65477_restoreMinWidth : function(oColumn) { 65478 if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) { 65479 this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px'; 65480 } 65481}, 65482 65483/** 65484 * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only 65485 * hide/show non-nested Columns, and top-level parent Columns (which will 65486 * hide/show all children Columns). 65487 * 65488 * @method hideColumn 65489 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column 65490 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a 65491 * ColumnSet key index. 65492 */ 65493hideColumn : function(oColumn) { 65494 if(!(oColumn instanceof YAHOO.widget.Column)) { 65495 oColumn = this.getColumn(oColumn); 65496 } 65497 // Only top-level Columns can get hidden due to issues in FF2 and SF3 65498 if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) { 65499 65500 var allrows = this.getTbodyEl().rows; 65501 var l = allrows.length; 65502 var allDescendants = this._oColumnSet.getDescendants(oColumn); 65503 65504 // Hide each nested Column 65505 for(var i=0, len=allDescendants.length; i<len; i++) { 65506 var thisColumn = allDescendants[i]; 65507 thisColumn.hidden = true; 65508 65509 // Style the head cell 65510 Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN); 65511 65512 // Does this Column have body cells? 65513 var thisKeyIndex = thisColumn.getKeyIndex(); 65514 if(thisKeyIndex !== null) { 65515 // Clear minWidth 65516 this._clearMinWidth(oColumn); 65517 65518 // Style the body cells 65519 for(var j=0;j<l;j++) { 65520 Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN); 65521 } 65522 } 65523 65524 this.fireEvent("columnHideEvent",{column:thisColumn}); 65525 YAHOO.log("Column \"" + oColumn.key + "\" hidden", "info", this.toString()); 65526 } 65527 65528 this._repaintOpera(); 65529 this._clearTrTemplateEl(); 65530 } 65531 else { 65532 YAHOO.log("Could not hide Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be hidden", "warn", this.toString()); 65533 } 65534}, 65535 65536/** 65537 * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only 65538 * hide/show non-nested Columns, and top-level parent Columns (which will 65539 * hide/show all children Columns). 65540 * 65541 * @method showColumn 65542 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column 65543 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a 65544 * ColumnSet key index. 65545 */ 65546showColumn : function(oColumn) { 65547 if(!(oColumn instanceof YAHOO.widget.Column)) { 65548 oColumn = this.getColumn(oColumn); 65549 } 65550 // Only top-level Columns can get hidden 65551 if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) { 65552 var allrows = this.getTbodyEl().rows; 65553 var l = allrows.length; 65554 var allDescendants = this._oColumnSet.getDescendants(oColumn); 65555 65556 // Show each nested Column 65557 for(var i=0, len=allDescendants.length; i<len; i++) { 65558 var thisColumn = allDescendants[i]; 65559 thisColumn.hidden = false;
65560 65561 // Unstyle the head cell 65562 Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN); 65563 65564 // Does this Column have body cells? 65565 var thisKeyIndex = thisColumn.getKeyIndex(); 65566 if(thisKeyIndex !== null) { 65567 // Restore minWidth 65568 this._restoreMinWidth(oColumn); 65569 65570 65571 // Unstyle the body cells 65572 for(var j=0;j<l;j++) { 65573 Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN); 65574 } 65575 } 65576 65577 this.fireEvent("columnShowEvent",{column:thisColumn}); 65578 YAHOO.log("Column \"" + oColumn.key + "\" shown", "info", this.toString()); 65579 } 65580 this._clearTrTemplateEl(); 65581 } 65582 else { 65583 YAHOO.log("Could not show Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be shown", "warn", this.toString()); 65584 } 65585}, 65586 65587/** 65588 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove 65589 * non-nested Columns, and top-level parent Columns (which will remove all 65590 * children Columns). 65591 * 65592 * @method removeColumn 65593 * @param oColumn {YAHOO.widget.Column} Column instance. 65594 * @return oColumn {YAHOO.widget.Column} Removed Column instance. 65595 */ 65596removeColumn : function(oColumn) { 65597 // Validate Column 65598 if(!(oColumn instanceof YAHOO.widget.Column)) { 65599 oColumn = this.getColumn(oColumn); 65600 } 65601 if(oColumn) { 65602 var nColTreeIndex = oColumn.getTreeIndex(); 65603 if(nColTreeIndex !== null) { 65604 // Which key index(es) 65605 var i, len, 65606 aKeyIndexes = oColumn.getKeyIndex(); 65607 // Must be a parent Column 65608 if(aKeyIndexes === null) { 65609 var descKeyIndexes = []; 65610 var allDescendants = this._oColumnSet.getDescendants(oColumn); 65611 for(i=0, len=allDescendants.length; i<len; i++) { 65612 // Is this descendant a key Column? 65613 var thisKey = allDescendants[i].getKeyIndex(); 65614 if(thisKey !== null) { 65615 descKeyIndexes[descKeyIndexes.length] = thisKey; 65616 } 65617 } 65618 if(descKeyIndexes.length > 0) { 65619 aKeyIndexes = descKeyIndexes; 65620 } 65621 } 65622 // Must be a key Column 65623 else { 65624 aKeyIndexes = [aKeyIndexes]; 65625 } 65626 65627 if(aKeyIndexes !== null) { 65628 // Sort the indexes so we can remove from the right 65629 aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);}); 65630 65631 // Destroy previous THEAD 65632 this._destroyTheadEl(); 65633 65634 // Create new THEAD 65635 var aOrigColumnDefs = this._oColumnSet.getDefinitions(); 65636 oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0]; 65637 this._initColumnSet(aOrigColumnDefs); 65638 this._initTheadEl(); 65639 65640 // Remove COL 65641 for(i=aKeyIndexes.length-1; i>-1; i--) { 65642 this._removeColgroupColEl(aKeyIndexes[i]); 65643 } 65644 65645 // Remove TD 65646 var allRows = this._elTbody.rows; 65647 if(allRows.length > 0) { 65648 var loopN = this.get("renderLoopSize"), 65649 loopEnd = allRows.length; 65650 this._oChainRender.add({ 65651 method: function(oArg) { 65652 if((this instanceof DT) && this._sId) { 65653 var i = oArg.nCurrentRow, 65654 len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length, 65655 aIndexes = oArg.aIndexes, 65656 j; 65657 for(; i < len; ++i) { 65658 for(j = aIndexes.length-1; j>-1; j--) { 65659 allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]); 65660 } 65661 } 65662 oArg.nCurrentRow = i; 65663 } 65664 }, 65665 iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1, 65666 argument: {nCurrentRow:0, aIndexes:aKeyIndexes}, 65667 scope: this, 65668 timeout: (loopN > 0) ? 0 : -1 65669 }); 65670 this._runRenderChain(); 65671 } 65672 65673 this.fireEvent("columnRemoveEvent",{column:oColumn}); 65674 YAHOO.log("Column \"" + oColumn.key + "\" removed", "info", this.toString()); 65675 return oColumn; 65676 } 65677 } 65678 } 65679 YAHOO.log("Could not remove Column \"" + oColumn.key + "\". Only non-ne
65679sted Columns can be removed", "warn", this.toString()); 65680}, 65681 65682/** 65683 * Inserts given Column at the index if given, otherwise at the end. NOTE: You 65684 * can only add non-nested Columns and top-level parent Columns. You cannot add 65685 * a nested Column to an existing parent. 65686 * 65687 * @method insertColumn 65688 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column 65689 * definition or a Column instance. 65690 * @param index {Number} (optional) New tree index. 65691 * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 65692 */ 65693insertColumn : function(oColumn, index) { 65694 // Validate Column 65695 if(oColumn instanceof YAHOO.widget.Column) { 65696 oColumn = oColumn.getDefinition(); 65697 } 65698 else if(oColumn.constructor !== Object) { 65699 YAHOO.log("Could not insert Column \"" + oColumn + "\" due to invalid argument", "warn", this.toString()); 65700 return; 65701 } 65702 65703 // Validate index or append new Column to the end of the ColumnSet 65704 var oColumnSet = this._oColumnSet; 65705 if(!lang.isValue(index) || !lang.isNumber(index)) { 65706 index = oColumnSet.tree[0].length; 65707 } 65708 65709 // Destroy previous THEAD 65710 this._destroyTheadEl(); 65711 65712 // Create new THEAD 65713 var aNewColumnDefs = this._oColumnSet.getDefinitions(); 65714 aNewColumnDefs.splice(index, 0, oColumn); 65715 this._initColumnSet(aNewColumnDefs); 65716 this._initTheadEl(); 65717 65718 // Need to refresh the reference 65719 oColumnSet = this._oColumnSet; 65720 var oNewColumn = oColumnSet.tree[0][index]; 65721 65722 // Get key index(es) for new Column 65723 var i, len, 65724 descKeyIndexes = []; 65725 var allDescendants = oColumnSet.getDescendants(oNewColumn); 65726 for(i=0, len=allDescendants.length; i<len; i++) { 65727 // Is this descendant a key Column? 65728 var thisKey = allDescendants[i].getKeyIndex(); 65729 if(thisKey !== null) { 65730 descKeyIndexes[descKeyIndexes.length] = thisKey; 65731 } 65732 } 65733 65734 if(descKeyIndexes.length > 0) { 65735 // Sort the indexes 65736 var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0]; 65737 65738 // Add COL 65739 for(i=descKeyIndexes.length-1; i>-1; i--) { 65740 this._insertColgroupColEl(descKeyIndexes[i]); 65741 } 65742 65743 // Add TD 65744 var allRows = this._elTbody.rows; 65745 if(allRows.length > 0) { 65746 var loopN = this.get("renderLoopSize"), 65747 loopEnd = allRows.length; 65748 65749 // Get templates for each new TD 65750 var aTdTemplates = [], 65751 elTdTemplate; 65752 for(i=0, len=descKeyIndexes.length; i<len; i++) { 65753 var thisKeyIndex = descKeyIndexes[i]; 65754 elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true); 65755 elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1)); 65756 aTdTemplates[thisKeyIndex] = elTdTemplate; 65757 } 65758 65759 this._oChainRender.add({ 65760 method: function(oArg) { 65761 if((this instanceof DT) && this._sId) { 65762 var i = oArg.nCurrentRow, j, 65763 descKeyIndexes = oArg.descKeyIndexes, 65764 len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length, 65765 nextSibling; 65766 for(; i < len; ++i) { 65767 nextSibling = allRows[i].childNodes[newIndex] || null; 65768 for(j=descKeyIndexes.length-1; j>-1; j--) { 65769 allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling); 65770 } 65771 } 65772 oArg.nCurrentRow = i; 65773 } 65774 }, 65775 iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1, 65776 argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes}, 65777 scope: this, 65778 timeout: (loopN > 0) ? 0 : -1 65779 }); 65780 this._runRenderChain(); 65781 } 65782 65783 this.fireEvent("columnInsertEvent",{column:oColumn,index:index}); 65784 YAHOO.log("Column \"" + oColumn.key + "\" inserted into index " + index, "info", this.toString()); 65785 return oNewColumn; 65786 } 65787}, 65788 65789/** 65790 * Removes given Column and inserts into given tree index. NOTE: You 65791 * can only reorder non-nested Columns and top-level parent Columns. You cannot 65792 * reorder a nested Column to an existing parent. 65793 * 65794 * @method reorderColumn 65795 * @param oColumn {YAHOO.widget.Column} Column instance. 65796 * @param index {Number} New tree index. 65797 * @return oColumn {YAHOO.widget.Column} Reordered Column instance. 65798 */ 65799reorderColumn : function(oColumn, index) { 65800 // Validate Column and new index 65801 if(!(oColumn instanceof YAHOO.widget.Column)) { 65802 oColumn = this.getColumn(oColumn); 65803 } 65804 if(oColumn && YAHOO.lang.isNumber(index)) { 65805 var nOrigTreeIndex = oColumn.getTreeIndex(); 65806 if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) { 65807 // Which key index(es) 65808 var i, len, 65809 aOrigKeyIndexes = oColumn.getKeyIndex(), 65810 allDescendants, 65811 descKeyIndexes = [], 65812 thisKey; 65813 // Must be a parent Column... 65814 if(aOrigKeyIndexes === null) { 65815 allDescendants = this._oColumnSet.getDescendants(oColumn); 65816 for(i=0, len=allDescendants.length; i<len; i++) { 65817 // Is this descendant a key Column? 65818 thisKey = allDescendants[i].getKeyIndex(); 65819 if(thisKey !== null) { 65820 descKeyIndexes[descKeyIndexes.length] = thisKey; 65821 } 65822 } 65823 if(descKeyIndexes.length > 0) { 65824 aOrigKeyIndexes = descKeyIndexes; 65825 } 65826 } 65827 // ...or else must be a key Column 65828 else {
65829 aOrigKeyIndexes = [aOrigKeyIndexes]; 65830 } 65831 65832 if(aOrigKeyIndexes !== null) { 65833 // Sort the indexes 65834 aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);}); 65835 65836 // Destroy previous THEAD 65837 this._destroyTheadEl(); 65838 65839 // Create new THEAD 65840 var aColumnDefs = this._oColumnSet.getDefinitions(); 65841 var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0]; 65842 aColumnDefs.splice(index, 0, oColumnDef); 65843 this._initColumnSet(aColumnDefs); 65844 this._initTheadEl(); 65845 65846 // Need to refresh the reference 65847 var oNewColumn = this._oColumnSet.tree[0][index]; 65848 65849 // What are new key index(es) 65850 var aNewKeyIndexes = oNewColumn.getKeyIndex(); 65851 // Must be a parent Column 65852 if(aNewKeyIndexes === null) { 65853 descKeyIndexes = []; 65854 allDescendants = this._oColumnSet.getDescendants(oNewColumn); 65855 for(i=0, len=allDescendants.length; i<len; i++) { 65856 // Is this descendant a key Column? 65857 thisKey = allDescendants[i].getKeyIndex(); 65858 if(thisKey !== null) { 65859 descKeyIndexes[descKeyIndexes.length] = thisKey; 65860 } 65861 } 65862 if(descKeyIndexes.length > 0) { 65863 aNewKeyIndexes = descKeyIndexes; 65864 } 65865 } 65866 // Must be a key Column 65867 else { 65868 aNewKeyIndexes = [aNewKeyIndexes]; 65869 } 65870 65871 // Sort the new indexes and grab the first one for the new location 65872 var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0]; 65873 65874 // Reorder COL 65875 this._reorderColgroupColEl(aOrigKeyIndexes, newIndex); 65876 65877 // Reorder TD 65878 var allRows = this._elTbody.rows; 65879 if(allRows.length > 0) { 65880 var loopN = this.get("renderLoopSize"), 65881 loopEnd = allRows.length; 65882 this._oChainRender.add({ 65883 method: function(oArg) { 65884 if((this instanceof DT) && this._sId) { 65885 var i = oArg.nCurrentRow, j, tmpTds, nextSibling, 65886 len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length, 65887 aIndexes = oArg.aIndexes, thisTr; 65888 // For each row 65889 for(; i < len; ++i) { 65890 tmpTds = []; 65891 thisTr = allRows[i]; 65892 65893 // Remove each TD 65894 for(j=aIndexes.length-1; j>-1; j--) { 65895 tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]])); 65896 } 65897 65898 // Insert each TD 65899 nextSibling = thisTr.childNodes[newIndex] || null; 65900 for(j=tmpTds.length-1; j>-1; j--) { 65901 thisTr.insertBefore(tmpTds[j], nextSibling); 65902 } 65903 } 65904 oArg.nCurrentRow = i; 65905 } 65906 }, 65907 iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1, 65908 argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes}, 65909 scope: this, 65910 timeout: (loopN > 0) ? 0 : -1 65911 }); 65912 this._runRenderChain(); 65913 } 65914 65915 this.fireEvent("columnReorderEvent",{column:oNewColumn, oldIndex:nOrigTreeIndex}); 65916 YAHOO.log("Column \"" + oNewColumn.key + "\" reordered", "info", this.toString()); 65917 return oNewColumn; 65918 } 65919 } 65920 } 65921 YAHOO.log("Could not reorder Column \"" + oColumn.key + "\". Only non-ne
65921sted Columns can be reordered", "warn", this.toString()); 65922}, 65923 65924/** 65925 * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only 65926 * select/unselect non-nested Columns, and bottom-level key Columns. 65927 * 65928 * @method selectColumn 65929 * @param column {HTMLElement | String | Number} DOM reference or ID string to a 65930 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index. 65931 */ 65932selectColumn : function(oColumn) { 65933 oColumn = this.getColumn(oColumn); 65934 if(oColumn && !oColumn.selected) { 65935 // Only bottom-level Columns can get hidden 65936 if(oColumn.getKeyIndex() !== null) { 65937 oColumn.selected = true; 65938 65939 // Update head cell 65940 var elTh = oColumn.getThEl(); 65941 Dom.addClass(elTh,DT.CLASS_SELECTED); 65942 65943 // Update body cells 65944 var allRows = this.getTbodyEl().rows; 65945 var oChainRender = this._oChainRender; 65946 oChainRender.add({ 65947 method: function(oArg) { 65948 if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) { 65949 Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED); 65950 } 65951 oArg.rowIndex++; 65952 }, 65953 scope: this, 65954 iterations: allRows.length, 65955 argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()} 65956 }); 65957 65958 this._clearTrTemplateEl(); 65959 65960 this._elTbody.style.display = "none"; 65961 this._runRenderChain(); 65962 this._elTbody.style.display = ""; 65963 65964 this.fireEvent("columnSelectEvent",{column:oColumn}); 65965 YAHOO.log("Column \"" + oColumn.key + "\" selected", "info", this.toString()); 65966 } 65967 else { 65968 YAHOO.log("Could not select Column \"" + oColumn.key + "\". Only non-nested Columns can be selected", "warn", this.toString()); 65969 } 65970 } 65971}, 65972 65973/** 65974 * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only 65975 * select/unselect non-nested Columns, and bottom-level key Columns. 65976 * 65977 * @method unselectColumn 65978 * @param column {HTMLElement | String | Number} DOM reference or ID string to a 65979 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index. 65980 */ 65981unselectColumn : function(oColumn) { 65982 oColumn = this.getColumn(oColumn); 65983 if(oColumn && oColumn.selected) { 65984 // Only bottom-level Columns can get hidden 65985 if(oColumn.getKeyIndex() !== null) { 65986 oColumn.selected = false; 65987 65988 // Update head cell 65989 var elTh = oColumn.getThEl(); 65990 Dom.removeClass(elTh,DT.CLASS_SELECTED); 65991 65992 // Update body cells 65993 var allRows = this.getTbodyEl().rows; 65994 var oChainRender = this._oChainRender; 65995 oChainRender.add({ 65996 method: function(oArg) { 65997 if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) { 65998 Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED); 65999 } 66000 oArg.rowIndex++; 66001 }, 66002 scope: this, 66003 iterations:allRows.length, 66004 argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()} 66005 }); 66006 66007 this._clearTrTemplateEl(); 66008 66009 this._elTbody.style.display = "none"; 66010 this._runRenderChain(); 66011 this._elTbody.style.display = ""; 66012 66013 this.fireEvent("columnUnselectEvent",{column:oColumn}); 66014 YAHOO.log("Column \"" + oColumn.key + "\" unselected", "info", this.toString()); 66015 } 66016 else { 66017 YAHOO.log("Could not unselect Column \"" + oColumn.key + "\". Only non-nested Columns can be unselected", "warn", this.toString()); 66018 } 66019 } 66020}, 66021 66022/** 66023 * Returns an array selected Column instances. 66024 * 66025 * @method getSelectedColumns 66026 * @return {YAHOO.widget.Column[]} Array of Column instances. 66027 */ 66028getSelectedColumns : function(oColumn) { 66029 var selectedColumns = []; 66030 var aKeys = this._oColumnSet.keys; 66031 for(var i=0,len=aKeys.length; i<len; i++) { 66032 if(aKeys[i].selected) { 66033 selectedColumns[selectedColumns.length] = aKeys[i]; 66034 } 66035 } 66036 return selectedColumns; 66037}, 66038 66039/** 66040 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column. 66041 * NOTE: You cannot highlight/unhighlight nested Columns. You can only 66042 * highlight/unhighlight non-nested Columns, and bottom-level key Columns. 66043 * 66044 * @method highlightColumn 66045 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
66046 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index. 66047 */ 66048highlightColumn : function(column) { 66049 var oColumn = this.getColumn(column); 66050 // Only bottom-level Columns can get highlighted 66051 if(oColumn && (oColumn.getKeyIndex() !== null)) { 66052 // Update head cell 66053 var elTh = oColumn.getThEl(); 66054 Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED); 66055 66056 // Update body cells 66057 var allRows = this.getTbodyEl().rows; 66058 var oChainRender = this._oChainRender; 66059 oChainRender.add({ 66060 method: function(oArg) { 66061 if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) { 66062 Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED); 66063 } 66064 oArg.rowIndex++; 66065 }, 66066 scope: this, 66067 iterations:allRows.length, 66068 argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}, 66069 timeout: -1 66070 }); 66071 this._elTbody.style.display = "none"; 66072 this._runRenderChain(); 66073 this._elTbody.style.display = ""; 66074 66075 this.fireEvent("columnHighlightEvent",{column:oColumn}); 66076 YAHOO.log("Column \"" + oColumn.key + "\" highlighed", "info", this.toString()); 66077 } 66078 else { 66079 YAHOO.log("Could not highlight Column \"" + oColumn.key + "\". Only non-nested Columns can be highlighted", "warn", this.toString()); 66080 } 66081}, 66082 66083/** 66084 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column. 66085 * NOTE: You cannot highlight/unhighlight nested Columns. You can only 66086 * highlight/unhighlight non-nested Columns, and bottom-level key Columns. 66087 * 66088 * @method unhighlightColumn 66089 * @param column {HTMLElement | String | Number} DOM reference or ID string to a 66090 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index. 66091 */ 66092unhighlightColumn : function(column) { 66093 var oColumn = this.getColumn(column); 66094 // Only bottom-level Columns can get highlighted 66095 if(oColumn && (oColumn.getKeyIndex() !== null)) { 66096 // Update head cell 66097 var elTh = oColumn.getThEl(); 66098 Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED); 66099 66100 // Update body cells 66101 var allRows = this.getTbodyEl().rows; 66102 var oChainRender = this._oChainRender; 66103 oChainRender.add({ 66104 method: function(oArg) { 66105 if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) { 66106 Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED); 66107 } 66108 oArg.rowIndex++; 66109 }, 66110 scope: this, 66111 iterations:allRows.length, 66112 argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}, 66113 timeout: -1 66114 }); 66115 this._elTbody.style.display = "none"; 66116 this._runRenderChain(); 66117 this._elTbody.style.display = ""; 66118 66119 this.fireEvent("columnUnhighlightEvent",{column:oColumn}); 66120 YAHOO.log("Column \"" + oColumn.key + "\" unhighlighted", "info", this.toString()); 66121 } 66122 else { 66123 YAHOO.log("Could not unhighlight Column \"" + oColumn.key + "\". Only non-nested Columns can be unhighlighted", "warn", this.toString()); 66124 } 66125}, 66126 66127 66128 66129 66130 66131 66132 66133 66134 66135 66136 66137 66138 66139 66140 66141 66142 66143 66144 66145 66146 66147 66148 66149 66150 66151 66152 66153 66154 66155 66156 66157 66158 66159 66160 66161 66162 66163 66164 66165 66166 66167 66168 66169 66170// ROW FUNCTIONS 66171 66172/** 66173 * Adds one new Record of data into the RecordSet at the index if given, 66174 * otherwise at the end. If the new Record is in page view, the 66175 * corresponding DOM elements are also updated. 66176 * 66177 * @method addRow 66178 * @param oData {Object} Object literal of data for the row. 66179 * @param index {Number} (optional) RecordSet position index at which to add data. 66180 */
66181addRow : function(oData, index) { 66182 if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) { 66183 YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString()); 66184 return; 66185 } 66186 66187 if(oData && lang.isObject(oData)) { 66188 var oRecord = this._oRecordSet.addRecord(oData, index); 66189 if(oRecord) { 66190 var recIndex; 66191 var oPaginator = this.get('paginator'); 66192 66193 // Paginated 66194 if (oPaginator) { 66195 // Update the paginator's totalRecords 66196 var totalRecords = oPaginator.get('totalRecords'); 66197 if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) { 66198 oPaginator.set('totalRecords',totalRecords + 1); 66199 } 66200 66201 recIndex = this.getRecordIndex(oRecord); 66202 var endRecIndex = (oPaginator.getPageRecords())[1]; 66203 66204 // New record affects the view 66205 if (recIndex <= endRecIndex) { 66206 // Defer UI updates to the render method 66207 this.render(); 66208 } 66209 66210 this.fireEvent("rowAddEvent", {record:oRecord}); 66211 YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString()); 66212 return; 66213 } 66214 // Not paginated 66215 else { 66216 recIndex = this.getRecordIndex(oRecord); 66217 if(lang.isNumber(recIndex)) { 66218 // Add the TR element 66219 this._oChainRender.add({ 66220 method: function(oArg) { 66221 if((this instanceof DT) && this._sId) { 66222 var oRecord = oArg.record; 66223 var recIndex = oArg.recIndex; 66224 var elNewTr = this._addTrEl(oRecord); 66225 if(elNewTr) { 66226 var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null; 66227 this._elTbody.insertBefore(elNewTr, elNext); 66228 66229 // Set FIRST/LAST 66230 if(recIndex === 0) { 66231 this._setFirstRow(); 66232 } 66233 if(elNext === null) { 66234 this._setLastRow(); 66235 } 66236 // Set EVEN/ODD 66237 this._setRowStripes(); 66238 66239 this.hideTableMessage(); 66240 66241 this.fireEvent("rowAddEvent", {record:oRecord}); 66242 YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString()); 66243 } 66244 } 66245 }, 66246 argument: {record: oRecord, recIndex: recIndex}, 66247 scope: this, 66248 timeout: (this.get("renderLoopSize") > 0) ? 0 : -1 66249 }); 66250 this._runRenderChain(); 66251 return; 66252 } 66253 } 66254 } 66255 } 66256 YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString()); 66257}, 66258 66259/** 66260 * Convenience method to add multiple rows. 66261 * 66262 * @method addRows 66263 * @param aData {Object[]} Array of object literal data for the rows. 66264 * @param index {Number} (optional) RecordSet position index at which to add data. 66265 */ 66266addRows : function(aData, index) { 66267 if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) { 66268 YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString()); 66269 return; 66270 } 66271 66272 if(lang.isArray(aData)) { 66273 var aRecords = this._oRecordSet.addRecords(aData, index); 66274 if(aRecords) { 66275 var recIndex = this.getRecordIndex(aRecords[0]); 66276 66277 // Paginated 66278 var oPaginator = this.get('paginator'); 66279 if (oPaginator) { 66280 // Update the paginator's totalRecords 66281 var totalRecords = oPaginator.get('totalRecords'); 66282 if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) { 66283 oPaginator.set('totalRecords',totalRecords + aRecords.length); 66284 } 66285 66286 var endRecIndex = (oPaginator.getPageRecords())[1]; 66287 66288 // At least one of the new records affects the view 66289 if (recIndex <= endRecIndex) { 66290 this.render(); 66291 } 66292 66293 this.fireEvent("rowsAddEvent", {records:aRecords}); 66294 YAHOO.log("Added " + aRecords.length + 66295 " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) + 66296 " with data " + lang.dump(aData), "info", this.toString()); 66297 return; 66298 } 66299 // Not paginated 66300 else { 66301 // Add the TR elements 66302 var loopN = this.get("renderLoopSize"); 66303 var loopEnd = recIndex + aData.length;
66304 var nRowsNeeded = (loopEnd - recIndex); // how many needed 66305 var isLast = (recIndex >= this._elTbody.rows.length); 66306 this._oChainRender.add({ 66307 method: function(oArg) { 66308 if((this instanceof DT) && this._sId) { 66309 var aRecords = oArg.aRecords, 66310 i = oArg.nCurrentRow, 66311 j = oArg.nCurrentRecord, 66312 len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd, 66313 df = document.createDocumentFragment(), 66314 elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null; 66315 for(; i < len; i++, j++) { 66316 df.appendChild(this._addTrEl(aRecords[j])); 66317 } 66318 this._elTbody.insertBefore(df, elNext); 66319 oArg.nCurrentRow = i; 66320 oArg.nCurrentRecord = j; 66321 } 66322 }, 66323 iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1, 66324 argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords}, 66325 scope: this, 66326 timeout: (loopN > 0) ? 0 : -1 66327 }); 66328 this._oChainRender.add({ 66329 method: function(oArg) { 66330 var recIndex = oArg.recIndex; 66331 // Set FIRST/LAST 66332 if(recIndex === 0) { 66333 this._setFirstRow(); 66334 } 66335 if(oArg.isLast) { 66336 this._setLastRow(); 66337 } 66338 // Set EVEN/ODD 66339 this._setRowStripes(); 66340 66341 this.fireEvent("rowsAddEvent", {records:aRecords}); 66342 YAHOO.log("Added " + aRecords.length + 66343 " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) + 66344 " with data " + lang.dump(aData), "info", this.toString()); 66345 }, 66346 argument: {recIndex: recIndex, isLast: isLast}, 66347 scope: this, 66348 timeout: -1 // Needs to run immediately after the DOM insertions above 66349 }); 66350 this._runRenderChain(); 66351 this.hideTableMessage(); 66352 return; 66353 } 66354 } 66355 } 66356 YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString()); 66357}, 66358 66359/** 66360 * For the given row, updates the associated Record with the given data. If the 66361 * row is on current page, the corresponding DOM elements are also updated. 66362 * 66363 * @method updateRow 66364 * @param row {YAHOO.widget.Record | Number | HTMLElement | String} 66365 * Which row to update: By Record instance, by Record's RecordSet 66366 * position index, by HTMLElement reference to the TR element, or by ID string 66367 * of the TR element. 66368 * @param oData {Object} Object literal of data for the row. 66369 */ 66370updateRow : function(row, oData) { 66371 var index = row; 66372 if (!lang.isNumber(index)) { 66373 index = this.getRecordIndex(row); 66374 } 66375 66376 // Update the Record 66377 if(lang.isNumber(index) && (index >= 0)) { 66378 var oRecordSet = this._oRecordSet, 66379 oldRecord = oRecordSet.getRecord(index); 66380 66381 if(oldRecord) { 66382 var updatedRecord = this._oRecordSet.setRecord(oData, index), 66383 elRow = this.getTrEl(oldRecord), 66384 // Copy data from the Record for the event that gets fired later 66385 oldData = oldRecord ? oldRecord.getData() : null; 66386 66387 if(updatedRecord) { 66388 // Update selected rows as necessary 66389 var tracker = this._aSelections || [], 66390 i=0, 66391 oldId = oldRecord.getId(), 66392 newId = updatedRecord.getId(); 66393 for(; i<tracker.length; i++) { 66394 if((tracker[i] === oldId)) { 66395 tracker[i] = newId; 66396 } 66397 else if(tracker[i].recordId === oldId) { 66398 tracker[i].recordId = newId; 66399 } 66400 } 66401 66402 // Update anchors as necessary 66403 if(this._oAnchorRecord && this._oAnchorRecord.getId() === oldId) { 66404 this._oAnchorRecord = updatedRecord; 66405 } 66406 if(this._oAnchorCell && this._oAnchorCell.record.getId() === oldId) { 66407 this._oAnchorCell.record = updatedRecord; 66408 } 66409 66410 // Update the TR only if row is on current page 66411 this._oChainRender.add({ 66412 method: function() { 66413 if((this instanceof DT) && this._sId) { 66414 // Paginated 66415 var oPaginator = this.get('paginator'); 66416 if (oPaginator) { 66417 var pageStartIndex = (oPaginator.getPageRecords())[0], 66418 pageLastIndex = (oPaginator.getPageRecords())[1]; 66419 66420 // At least one of the new records affects the view 66421 if ((index >= pageStartIndex) || (index <= pageLastIndex)) { 66422 this.render(); 66423 } 66424 } 66425 else { 66426 if(elRow) { 66427 this._updateTrEl(elRow, updatedRecord); 66428 } 66429 else { 66430 this.getTbodyEl().appendChild(this._addTrEl(updatedRecord)); 66431 } 66432 } 66433 this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData}
66433); 66434 YAHOO.log("DataTable row updated: Record ID = " + updatedRecord.getId() + 66435 ", Record index = " + this.getRecordIndex(updatedRecord) + 66436 ", page row index = " + this.getTrIndex(updatedRecord), "info", this.toString()); 66437 } 66438 }, 66439 scope: this, 66440 timeout: (this.get("renderLoopSize") > 0) ? 0 : -1 66441 }); 66442 this._runRenderChain(); 66443 return; 66444 } 66445 } 66446 } 66447 YAHOO.log("Could not update row " + row + " with the data : " + lang.dump(oData), "warn", this.toString()); 66448 return; 66449}, 66450 66451/** 66452 * Starting with the given row, updates associated Records with the given data. 66453 * The number of rows to update are determined by the array of data provided. 66454 * Undefined data (i.e., not an object literal) causes a row to be skipped. If 66455 * any of the rows are on current page, the corresponding DOM elements are also 66456 * updated. 66457 * 66458 * @method updateRows 66459 * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String} 66460 * Starting row to update: By Record instance, by Record's RecordSet 66461 * position index, by HTMLElement reference to the TR element, or by ID string 66462 * of the TR element. 66463 * @param aData {Object[]} Array of object literal of data for the rows. 66464 */ 66465updateRows : function(startrow, aData) { 66466 if(lang.isArray(aData)) { 66467 var startIndex = startrow, 66468 oRecordSet = this._oRecordSet, 66469 lastRowIndex = oRecordSet.getLength(); 66470 66471 if (!lang.isNumber(startrow)) { 66472 startIndex = this.getRecordIndex(startrow); 66473 } 66474 66475 if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) { 66476 var lastIndex = startIndex + aData.length, 66477 aOldRecords = oRecordSet.getRecords(startIndex, aData.length), 66478 aNewRecords = oRecordSet.setRecords(aData, startIndex); 66479 if(aNewRecords) { 66480 var tracker = this._aSelections || [], 66481 i=0, j, newRecord, newId, oldId, 66482 anchorRecord = this._oAnchorRecord ? this._oAnchorRecord.getId() : null, 66483 anchorCell = this._oAnchorCell ? this._oAnchorCell.record.getId() : null; 66484 for(; i<aOldRecords.length; i++) { 66485 oldId = aOldRecords[i].getId(); 66486 newRecord = aNewRecords[i]; 66487 newId = newRecord.getId(); 66488 66489 // Update selected rows as necessary 66490 for(j=0; j<tracker.length; j++) { 66491 if((tracker[j] === oldId)) { 66492 tracker[j] = newId; 66493 } 66494 else if(tracker[j].recordId === oldId) { 66495 tracker[j].recordId = newId; 66496 } 66497 } 66498 66499 // Update anchors as necessary 66500 if(anchorRecord && anchorRecord === oldId) { 66501 this._oAnchorRecord = newRecord; 66502 } 66503 if(anchorCell && anchorCell === oldId) { 66504 this._oAnchorCell.record = newRecord; 66505 } 66506 } 66507 66508 // Paginated 66509 var oPaginator = this.get('paginator'); 66510 if (oPaginator) { 66511 var pageStartIndex = (oPaginator.getPageRecords())[0], 66512 pageLastIndex = (oPaginator.getPageRecords())[1]; 66513 66514 // At least one of the new records affects the view 66515 if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) { 66516 this.render(); 66517 } 66518 66519 this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords}); 66520 YAHOO.log("Added " + aNewRecords.length + 66521 " rows starting at index " + startIndex + 66522 " with data " + lang.dump(aData), "info", this.toString()); 66523 return; 66524 } 66525 // Not paginated 66526 else { 66527 // Update the TR elements 66528 var loopN = this.get("renderLoopSize"), 66529 rowCount = aData.length, // how many needed 66530 isLast = (lastIndex >= lastRowIndex), 66531 isAdding = (lastIndex > lastRowIndex); 66532 66533 this._oChainRender.add({ 66534 method: function(oArg) { 66535 if((this instanceof DT) && this._sId) { 66536 var aRecords = oArg.aRecords, 66537 i = oArg.nCurrentRow, 66538 j = oArg.nDataPointer, 66539 len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length; 66540 66541 for(; i < len; i++,j++) { 66542 if(isAdding && (i>=lastRowIndex)) { 66543 this._elTbody.appendChild(this._addTrEl(aRecords[j])); 66544 } 66545 else { 66546 this._updateTrEl(this._elTbody.rows[i], aRecords[j]); 66547 } 66548 } 66549 oArg.nCurrentRow = i; 66550 oArg.nDataPointer = j; 66551 } 66552 }, 66553 iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1, 66554 argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding}, 66555 scope: this, 66556 timeout: (loopN > 0) ? 0 : -1 66557 }); 66558 this._oChainRender.add({ 66559 method: function(oArg) { 66560 var recIndex = oArg.recIndex; 66561 // Set FIRST/LAST 66562 if(recIndex === 0) { 66563 this._setFirstRow(); 66564 } 66565 if(oArg.isLast) { 66566 this._setLastRow(); 66567 } 66568 // Set EVEN/ODD 66569 this._setRowStripes(); 66570 66571 this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords}); 66572 YAHOO.log("Added " + aNewRecords.length + 66573 " rows starting at index " + startIndex + 66574 " with data " + lang.dump(aData), "info", this.toString()); 66575 }, 66576 argument: {recIndex: startIndex, isLast: isLast}, 66577 scope: this, 66578 timeout: -1 // Needs to run immediately after the DOM insertions above 66579 }); 66580 this._runRenderChain(); 66581 this.hideTableMessage(); 66582 return; 66583 } 66584 } 66585 } 66586 } 66587 YAHOO.log("Could not update rows at " + startrow + " with " + lang.dump(aData), "warn", this.toString()); 66588}, 66589 66590/** 66591 * Deletes the given row's Record from the RecordSet. If the row is on current page, 66592 * the corresponding DOM elements are also deleted. 66593 * 66594 * @method deleteRow 66595 * @param row {HTMLElement | String | Number} DOM element reference or ID string 66596 * to DataTable page element or RecordSet index. 66597 */ 66598deleteRow : function(row) { 66599 var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row); 66600 if(lang.isNumber(nRecordIndex)) { 66601 var oRecord = this.getRecord(nRecordIndex); 66602 if(oRecord) { 66603 var nTrIndex = this.getTrIndex(nRecordIndex); 66604 66605 // Remove from selection tracker if there 66606 var sRecordId = oRecord.getId(); 66607 var tracker = this._aSelections || []; 66608 for(var j=tracker.length-1; j>-1; j--) { 66609 if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) || 66610 (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) { 66611 tracker.splice(j,1); 66612 } 66613 } 66614 66615 // Delete Record from RecordSet 66616 var oData = this._oRecordSet.deleteRecord(nRecordIndex); 66617 66618 // Update the UI 66619 if(oData) { 66620 // If paginated and the deleted row was on this or a prior page, just 66621 // re-render 66622 var oPaginator = this.get('paginator'); 66623 if (oPaginator) { 66624 // Update the paginator's totalRecords 66625 var totalRecords = oPaginator.get('totalRecords'), 66626 // must capture before the totalRecords change because 66627 // Paginator shifts to previous page automatically 66628 rng = oPaginator.getPageRecords(); 66629 66630 if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) { 66631 oPaginator.set('totalRecords',totalRecords - 1); 66632 } 66633 66634 // The deleted record was on this or a prior page, re-render 66635 if (!rng || nRecordIndex <= rng[1]) { 66636 this.render(); 66637 } 66638 66639 this._oChainRender.add({ 66640 method: function() { 66641 if((this instanceof DT) && this._sId) { 66642 this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex}); 66643 YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString()); 66644 } 66645 }, 66646 scope: this, 66647 timeout: (this.get("renderLoopSize") > 0) ? 0 : -1 66648 }); 66649 this._runRenderChain(); 66650 } 66651 // Not paginated 66652 else { 66653 if(lang.isNumber(nTrIndex)) { 66654 this._oChainRender.add({ 66655 method: function() { 66656 if((this instanceof DT) && this._sId) { 66657 var isLast = (nRecordIndex === this._oRecordSet.getLength());//(nTrIndex == this.getLastTrEl().sectionRowIndex); 66658 this._deleteTrEl(nTrIndex); 66659 66660 // Post-delete tasks 66661 if(this._elTbody.rows.length > 0) { 66662 // Set FIRST/LAST 66663 if(nTrIndex === 0) { 66664 this._setFirstRow(); 66665 } 66666 if(isLast) { 66667 this._setLastRow(); 66668 } 66669 // Set EVEN/ODD 66670 if(nTrIndex != this._elTbody.rows.length) { 66671 this._setRowStripes(nTrIndex); 66672 } 66673 } 66674 66675 this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex}); 66676 YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString()); 66677 } 66678 }, 66679 scope: this, 66680 timeout: (this.get("renderLoopSize") > 0) ? 0 : -1 66681 }); 66682 this._runRenderChain(); 66683 return; 66684 } 66685 } 66686 } 66687 } 66688 } 66689 YAHOO.log("Could not delete row: " + row, "warn", this.toString()); 66690 return null; 66691}, 66692 66693/** 66694 * Convenience method to delete multiple rows. 66695 * 66696 * @method deleteRows 66697 * @param row {HTMLElement | String | Number} DOM element reference or ID string 66698 * to DataTable page element or RecordSet index. 66699 * @param count {Number} (optional) How many rows to delete. A negative value 66700 * will delete towards the beginning. 66701 */ 66702deleteRows : function(row, count) { 66703 var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row); 66704 if(lang.isNumber(nRecordIndex)) { 66705 var oRecord = this.getRecord(nRecordIndex); 66706 if(oRecord) { 66707 var nTrIndex = this.getTrIndex(nRecordIndex); 66708 66709 // Remove from selection tracker if there 66710 var sRecordId = oRecord.getId(); 66711 var tracker = this._aSelections || []; 66712 for(var j=tracker.length-1; j>-1; j--) { 66713 if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) || 66714 (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) { 66715 tracker.splice(j,1); 66716 } 66717 } 66718 66719 // Delete Record from RecordSet 66720 var highIndex = nRecordIndex; 66721 var lowIndex = nRecordIndex; 66722 66723 // Validate count and account for negative value 66724 if(count && lang.isNumber(count)) { 66725 highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex; 66726 lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1; 66727 count = (count > 0) ? count : count*-1; 66728 if(lowIndex < 0) { 66729 lowIndex = 0; 66730 count = highIndex - lowIndex + 1; 66731 } 66732 } 66733 else { 66734 count = 1; 66735 } 66736 66737 var aData = this._oRecordSet.deleteRecords(lowIndex, count); 66738 66739 // Update the UI 66740 if(aData) { 66741 var oPaginator = this.get('paginator'), 66742 loopN = this.get("renderLoopSize"); 66743 // If paginated and the deleted row was on this or a prior page, just 66744 // re-render 66745 if (oPaginator) { 66746 // Update the paginator's totalRecords 66747 var totalRecords = oPaginator.get('totalRecords'), 66748 // must capture before the totalRecords change because 66749 // Paginator shifts to previous page automatically 66750 rng = oPaginator.getPageRecords(); 66751 66752 if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) { 66753 oPaginator.set('totalRecords',totalRecords - aData.length); 66754 } 66755 66756 // The records were on this or a prior page, re-render 66757 if (!rng || lowIndex <= rng[1]) { 66758 this.render(); 66759 } 66760 66761 this._oChainRender.add({ 66762 method: function(oArg) { 66763 if((this instanceof DT) && this._sId) { 66764 this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count}); 66765 YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString()); 66766 } 66767 }, 66768 scope: this, 66769 timeout: (loopN > 0) ? 0 : -1 66770 }); 66771 this._runRenderChain(); 66772 return; 66773 } 66774 // Not paginated 66775 else { 66776 if(lang.isNumber(nTrIndex)) { 66777 // Delete the TR elements starting with highest index 66778 var loopEnd = lowIndex; 66779
66779 var nRowsNeeded = count; // how many needed 66780 this._oChainRender.add({ 66781 method: function(oArg) { 66782 if((this instanceof DT) && this._sId) { 66783 var i = oArg.nCurrentRow, 66784 len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1; 66785 for(; i>len; --i) { 66786 this._deleteTrEl(i); 66787 } 66788 oArg.nCurrentRow = i; 66789 } 66790 }, 66791 iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1, 66792 argument: {nCurrentRow:highIndex}, 66793 scope: this, 66794 timeout: (loopN > 0) ? 0 : -1 66795 }); 66796 this._oChainRender.add({ 66797 method: function() { 66798 // Post-delete tasks 66799 if(this._elTbody.rows.length > 0) { 66800 this._setFirstRow(); 66801 this._setLastRow(); 66802 this._setRowStripes(); 66803 } 66804 66805 this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count}); 66806 YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString()); 66807 }, 66808 scope: this, 66809 timeout: -1 // Needs to run immediately after the DOM deletions above 66810 }); 66811 this._runRenderChain(); 66812 return; 66813 } 66814 } 66815 } 66816 } 66817 } 66818 YAHOO.log("Could not delete " + count + " rows at row " + row, "warn", this.toString()); 66819 return null; 66820}, 66821 66822 66823 66824 66825 66826 66827 66828 66829 66830 66831 66832 66833 66834 66835 66836 66837 66838 66839 66840 66841 66842 66843 66844 66845 66846 66847 66848 66849 66850 66851 66852 66853 66854 66855 66856 66857 66858 66859 66860 66861 66862 66863 66864 66865 66866 66867// CELL FUNCTIONS 66868 66869/** 66870 * Outputs markup into the given TD based on given Record. 66871 * 66872 * @method formatCell 66873 * @param elLiner {HTMLElement} The liner DIV element within the TD. 66874 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance. 66875 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance. 66876 */ 66877formatCell : function(elLiner, oRecord, oColumn) { 66878 if(!oRecord) { 66879 oRecord = this.getRecord(elLiner); 66880 } 66881 if(!oColumn) { 66882 oColumn = this.getColumn(this.getCellIndex(elLiner.parentNode)); 66883 } 66884 66885 if(oRecord && oColumn) { 66886 var sField = oColumn.field; 66887 var oData = oRecord.getData(sField); 66888 66889 var fnFormatter = typeof oColumn.formatter === 'function' ? 66890 oColumn.formatter : 66891 DT.Formatter[oColumn.formatter+''] || 66892 DT.Formatter.defaultFormatter; 66893 66894 // Apply special formatter 66895 if(fnFormatter) { 66896 fnFormatter.call(this, elLiner, oRecord, oColumn, oData); 66897 } 66898 else { 66899 elLiner.innerHTML = oData; 66900 } 66901 66902 this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elLiner}); 66903 } 66904 else { 66905 YAHOO.log("Could not format cell " + elLiner, "error", this.toString()); 66906 } 66907}, 66908 66909/** 66910 * For the given row and column, updates the Record with the given data. If the 66911 * cell is on current page, the corresponding DOM elements are also updated. 66912 * 66913 * @method updateCell 66914 * @param oRecord {YAHOO.widget.Record} Record instance. 66915 * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index. 66916 * @param oData {Object} New data value for the cell. 66917 * @param skipRender {Boolean} Skips render step. Editors that update multiple 66918 * cells in ScrollingDataTable should render only on the last call to updateCell(). 66919 */ 66920updateCell : function(oRecord, oColumn, oData, skipRender) { 66921 // Validate Column and Record 66922 oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn); 66923 if(oColumn && oColumn.getField() && (oRecord instanceof YAHOO.widget.Record)) { 66924 var sKey = oColumn.getField(), 66925 66926 // Copy data from the Record for the event that gets fired later 66927 //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData()); 66928 oldData = oRecord.getData(sKey); 66929 66930 // Update Record with new data 66931 this._oRecordSet.updateRecordValue(oRecord, sKey, oData); 66932 66933 // Update the TD only if row is on current page 66934 var elTd = this.getTdEl({record: oRecord, column: oColumn}); 66935 if(elTd) { 66936 this._oChainRender.add({ 66937 method: function() { 66938 if((this instanceof DT) && this._sId) { 66939 this.formatCell(elTd.firstChild, oRecord, oColumn); 66940 this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData}
66940); 66941 YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() + 66942 ", Record index = " + this.getRecordIndex(oRecord) + 66943 ", page row index = " + this.getTrIndex(oRecord) + 66944 ", Column key = " + oColumn.getKey(), "info", this.toString()); 66945 } 66946 }, 66947 scope: this, 66948 timeout: (this.get("renderLoopSize") > 0) ? 0 : -1 66949 }); 66950 // Bug 2529024 66951 if(!skipRender) { 66952 this._runRenderChain(); 66953 } 66954 } 66955 else { 66956 this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData}); 66957 YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() + 66958 ", Record index = " + this.getRecordIndex(oRecord) + 66959 ", page row index = " + this.getTrIndex(oRecord) + 66960 ", Column key = " + oColumn.getKey(), "info", this.toString()); 66961 } 66962 } 66963}, 66964 66965 66966 66967 66968 66969 66970 66971 66972 66973 66974 66975 66976 66977 66978 66979 66980 66981 66982 66983 66984 66985 66986 66987 66988 66989 66990 66991 66992 66993 66994 66995 66996 66997 66998 66999 67000 67001 67002 67003 67004 67005 67006 67007 67008 67009 67010 67011 67012 67013 67014 67015// PAGINATION 67016/** 67017 * Method executed during set() operation for the "paginator" attribute. 67018 * Adds and/or severs event listeners between DataTable and Paginator 67019 * 67020 * @method _updatePaginator 67021 * @param newPag {Paginator} Paginator instance (or null) for DataTable to use 67022 * @private 67023 */ 67024_updatePaginator : function (newPag) { 67025 var oldPag = this.get('paginator'); 67026 if (oldPag && newPag !== oldPag) { 67027 oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true); 67028 } 67029 if (newPag) {
67030 newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true); 67031 } 67032}, 67033 67034/** 67035 * Update the UI infrastructure in response to a "paginator" attribute change. 67036 * 67037 * @method _handlePaginatorChange 67038 * @param e {Object} Change event object containing keys 'type','newValue', 67039 * and 'prevValue' 67040 * @private 67041 */ 67042_handlePaginatorChange : function (e) { 67043 if (e.prevValue === e.newValue) { return; } 67044 67045 var newPag = e.newValue, 67046 oldPag = e.prevValue, 67047 containers = this._defaultPaginatorContainers(); 67048 67049 if (oldPag) { 67050 if (oldPag.getContainerNodes()[0] == containers[0]) { 67051 oldPag.set('containers',[]); 67052 } 67053 oldPag.destroy(); 67054 67055 // Convenience: share the default containers if possible. 67056 // Otherwise, remove the default containers from the DOM. 67057 if (containers[0]) { 67058 if (newPag && !newPag.getContainerNodes().length) { 67059 newPag.set('containers',containers); 67060 } else { 67061 // No new Paginator to use existing containers, OR new 67062 // Paginator has configured containers. 67063 for (var i = containers.length - 1; i >= 0; --i) { 67064 if (containers[i]) { 67065 containers[i].parentNode.removeChild(containers[i]); 67066 } 67067 } 67068 } 67069 } 67070 } 67071 67072 if (!this._bInit) { 67073 this.render(); 67074 67075 } 67076 67077 if (newPag) { 67078 this.renderPaginator(); 67079 } 67080 67081}, 67082 67083/** 67084 * Returns the default containers used for Paginators. If create param is 67085 * passed, the containers will be created and added to the DataTable container. 67086 * 67087 * @method _defaultPaginatorContainers 67088 * @param create {boolean} Create the default containers if not found 67089 * @private 67090 */ 67091_defaultPaginatorContainers : function (create) { 67092 var above_id = this._sId + '-paginator0', 67093 below_id = this._sId + '-paginator1', 67094 above = Dom.get(above_id), 67095 below = Dom.get(below_id); 67096 67097 if (create && (!above || !below)) { 67098 // One above and one below the table 67099 if (!above) { 67100 above = document.createElement('div'); 67101 above.id = above_id; 67102 Dom.addClass(above, DT.CLASS_PAGINATOR); 67103 67104 this._elContainer.insertBefore(above,this._elContainer.firstChild); 67105 } 67106 67107 if (!below) { 67108 below = document.createElement('div'); 67109 below.id = below_id; 67110 Dom.addClass(below, DT.CLASS_PAGINATOR); 67111 67112 this._elContainer.appendChild(below); 67113 } 67114 } 67115 67116 return [above,below]; 67117}, 67118 67119/** 67120 * Calls Paginator's destroy() method 67121 * 67122 * @method _destroyPaginator 67123 * @private 67124 */ 67125_destroyPaginator : function () { 67126 var oldPag = this.get('paginator'); 67127 if (oldPag) { 67128 oldPag.destroy(); 67129 } 67130}, 67131 67132/** 67133 * Renders the Paginator to the DataTable UI 67134 * 67135 * @method renderPaginator 67136 */ 67137renderPaginator : function () { 67138 var pag = this.get("paginator"); 67139 if (!pag) { return; } 67140 67141 // Add the containers if the Paginator is not configured with containers 67142 if (!pag.getContainerNodes().length) { 67143 pag.set('containers',this._defaultPaginatorContainers(true)); 67144 } 67145 67146 pag.render(); 67147}, 67148 67149/** 67150 * Overridable method gives implementers a hook to show loading message before 67151 * changing Paginator value. 67152 * 67153 * @method doBeforePaginatorChange 67154 * @param oPaginatorState {Object} An object literal describing the proposed pagination state. 67155 * @return {Boolean} Return true to continue changing Paginator value. 67156 */ 67157doBeforePaginatorChange : function(oPaginatorState) { 67158 this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING); 67159 return true; 67160}, 67161 67162/** 67163 * Responds to new Pagination states. By default, updates the UI to reflect the 67164 * new state. If "dynamicData" is true, current selections are purged before 67165 * a request is sent to the DataSource for data for the new state (using the 67166 * request returned by "generateRequest()"). 67167 * 67168 * @method onPaginatorChangeRequest 67169 * @param oPaginatorState {Object} An object literal describing the proposed pagination state. 67170 */ 67171onPaginatorChangeRequest : function (oPaginatorState) { 67172 var ok = this.doBeforePaginatorChange(oPaginatorState); 67173 if(ok) { 67174 // Server-side pagination 67175 if(this.get("dynamicData")) { 67176 // Get the current state
67177 var oState = this.getState(); 67178 67179 // Update pagination values 67180 oState.pagination = oPaginatorState; 67181 67182 // Get the request for the new state 67183 var request = this.get("generateRequest")(oState, this); 67184 67185 // Purge selections 67186 this.unselectAllRows(); 67187 this.unselectAllCells(); 67188 67189 // Get the new data from the server 67190 var callback = { 67191 success : this.onDataReturnSetRows, 67192 failure : this.onDataReturnSetRows, 67193 argument : oState, // Pass along the new state to the callback 67194 scope : this 67195 }; 67196 this._oDataSource.sendRequest(request, callback); 67197 } 67198 // Client-side pagination 67199 else { 67200 // Set the core pagination values silently (the second param) 67201 // to avoid looping back through the changeRequest mechanism 67202 oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true); 67203 oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true); 67204 67205 // Update the UI 67206 this.render(); 67207 } 67208 } 67209 else { 67210 YAHOO.log("Could not change Paginator value \"" + oPaginatorState + "\"", "warn", this.toString()); 67211 } 67212}, 67213 67214 67215 67216 67217 67218 67219 67220 67221 67222 67223 67224 67225 67226 67227 67228 67229 67230 67231 67232 67233 67234 67235 67236 67237 67238 67239 67240 67241 67242 67243 67244 67245 67246 67247 67248 67249 67250 67251 67252 67253 67254 67255 67256 67257 67258 67259 67260 67261 67262 67263// SELECTION/HIGHLIGHTING 67264 67265/* 67266 * Reference to last highlighted cell element 67267 * 67268 * @property _elLastHighlightedTd 67269 * @type HTMLElement 67270 * @private 67271 */ 67272_elLastHighlightedTd : null, 67273 67274/* 67275 * ID string of last highlighted row element 67276 * 67277 * @property _sLastHighlightedTrElId 67278 * @type String 67279 * @private 67280 */ 67281//_sLastHighlightedTrElId : null, 67282 67283/** 67284 * Array to track row selections (by sRecordId) and/or cell selections 67285 * (by {recordId:sRecordId, columnKey:sColumnKey}) 67286 * 67287 * @property _aSelections 67288 * @type Object[] 67289 * @private 67290 */ 67291_aSelections : null, 67292 67293/** 67294 * Record instance of the row selection anchor. 67295 * 67296 * @property _oAnchorRecord 67297 * @type YAHOO.widget.Record 67298 * @private 67299 */ 67300_oAnchorRecord : null, 67301 67302/** 67303 * Object literal representing cell selection anchor: 67304 * {recordId:sRecordId, columnKey:sColumnKey}. 67305 * 67306 * @property _oAnchorCell 67307 * @type Object 67308 * @private 67309 */ 67310_oAnchorCell : null, 67311 67312/** 67313 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED 67314 * from all TR elements on the page. 67315 * 67316 * @method _unselectAllTrEls 67317 * @private 67318 */ 67319_unselectAllTrEls : function() { 67320 var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody); 67321 Dom.removeClass(selectedRows, DT.CLASS_SELECTED); 67322}, 67323 67324/** 67325 * Returns object literal of values that represent the selection trigger. Used 67326 * to determine selection behavior resulting from a key event. 67327 * 67328 * @method _getSelectionTrigger 67329 * @private 67330 */ 67331_getSelectionTrigger : function() { 67332 var sMode = this.get("selectionMode"); 67333 var oTrigger = {}; 67334 var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex; 67335 67336 // Cell mode 67337 if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) { 67338 oTriggerCell = this.getLastSelectedCell(); 67339 // No selected cells found 67340 if(!oTriggerCell) { 67341 return null; 67342 } 67343 else { 67344 oTriggerRecord = this.getRecord(oTriggerCell.recordId); 67345 nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord); 67346 elTriggerRow = this.getTrEl(oTriggerRecord); 67347 nTriggerTrIndex = this.getTrIndex(elTriggerRow); 67348 67349 // Selected cell not found on this page 67350 if(nTriggerTrIndex === null) { 67351 return null; 67352 } 67353 else { 67354 oTrigger.record = oTriggerRecord; 67355 oTrigger.recordIndex = nTriggerRecordIndex; 67356 oTrigger.el = this.getTdEl(oTriggerCell); 67357 oTrigger.trIndex = nTriggerTrIndex; 67358 oTrigger.column = this.getColumn(oTriggerCell.columnKey); 67359 oTrigger.colKeyIndex = oTrigger.column.getKeyIndex(); 67360 oTrigger.cell = oTriggerCell; 67361 return oTrigger; 67362 } 67363 } 67364 } 67365 // Row mode 67366 else { 67367 oTriggerRecord = this.getLastSelectedRecord(); 67368 // No selected rows found 67369 if(!oTriggerRecord) { 67370 return null; 67371 } 67372 else { 67373 // Selected row found, but is it on current page? 67374 oTriggerRecord = this.getRecord(oTriggerRecord); 67375 nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord); 67376 elTriggerRow = this.getTrEl(oTriggerRecord); 67377 nTriggerTrIndex = this.getTrIndex(elTriggerRow); 67378 67379 // Selected row not found on this page 67380 if(nTriggerTrIndex === null) { 67381 return null; 67382 } 67383 else { 67384 oTrigger.record = oTriggerRecord; 67385 oTrigger.recordIndex = nTriggerRecordIndex; 67386 oTrigger.el = elTriggerRow; 67387 oTrigger.trIndex = nTriggerTrIndex; 67388 return oTrigger; 67389 } 67390 } 67391 } 67392}, 67393 67394/** 67395 * Returns object literal of values that represent the selection anchor. Used 67396 * to determine selection behavior resulting from a user event. 67397 * 67398 * @method _getSelectionAnchor 67399 * @param oTrigger {Object} (Optional) Object literal of selection trigger values 67400 * (for key events). 67401 * @private 67402 */ 67403_getSelectionAnchor : function(oTrigger) { 67404 var sMode = this.get("selectionMode"); 67405 var oAnchor = {}; 67406 var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex; 67407 67408 // Cell mode 67409 if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) { 67410 // Validate anchor cell 67411 var oAnchorCell = this._oAnchorCell; 67412 if(!oAnchorCell) { 67413 if(oTrigger) { 67414 oAnchorCell = this._oAnchorCell = oTrigger.cell; 67415 } 67416 else { 67417 return null; 67418 } 67419 } 67420 oAnchorRecord = this._oAnchorCell.record; 67421 nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord); 67422 nAnchorTrIndex = this.getTrIndex(oAnchorRecord); 67423 // If anchor cell is not on this page... 67424 if(nAnchorTrIndex === null) { 67425 // ...set TR index equal to top TR 67426 if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) { 67427 nAnchorTrIndex = 0; 67428 } 67429 // ...set TR index equal to bottom TR 67430 else { 67431 nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl()); 67432 } 67433 } 67434 67435 oAnchor.record = oAnchorRecord; 67436 oAnchor.recordIndex = nAnchorRecordIndex; 67437 oAnchor.trIndex = nAnchorTrIndex; 67438 oAnchor.column = this._oAnchorCell.column; 67439 oAnchor.colKeyIndex = oAnchor.column.getKeyIndex(); 67440 oAnchor.cell = oAnchorCell; 67441 return oAnchor; 67442 } 67443 // Row mode 67444 else { 67445 oAnchorRecord = this._oAnchorRecord; 67446 if(!oAnchorRecord) { 67447 if(oTrigger) { 67448 oAnchorRecord = this._oAnchorRecord = oTrigger.record; 67449 } 67450 else { 67451 return null; 67452 } 67453 } 67454 67455 nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord); 67456 nAnchorTrIndex = this.getTrIndex(oAnchorRecord); 67457 // If anchor row is not on this page... 67458 if(nAnchorTrIndex === null) { 67459 // ...set TR index equal to top TR 67460 if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) { 67461 nAnchorTrIndex = 0; 67462 } 67463 // ...set TR index equal to bottom TR 67464 else { 67465 nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl()); 67466 } 67467 } 67468 67469 oAnchor.record = oAnchorRecord; 67470 oAnchor.recordIndex = nAnchorRecordIndex; 67471 oAnchor.trIndex = nAnchorTrIndex; 67472 return oAnchor; 67473 } 67474}, 67475 67476/** 67477 * Determines selection behavior resulting from a mouse event when selection mode 67478 * is set to "standard". 67479 * 67480 * @method _handleStandardSelectionByMouse 67481 * @param oArgs.event {HTMLEvent} Event object. 67482 * @param oArgs.target {HTMLElement} Target element. 67483 * @private 67484 */ 67485_handleStandardSelectionByMouse : function(oArgs) { 67486 var elTarget = oArgs.target; 67487 67488 // Validate target row 67489 var elTargetRow = this.getTrEl(elTarget); 67490 if(elTargetRow) { 67491 var e = oArgs.event; 67492 var bSHIFT = e.shiftKey; 67493 var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey); 67494 67495 var oTargetRecord = this.getRecord(elTargetRow); 67496 var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord); 67497 67498 var oAnchor = this._getSelectionAnchor(); 67499 67500 var i; 67501 67502 // Both SHIFT and CTRL 67503 if(bSHIFT && bCTRL) { 67504 // Validate anchor 67505 if(oAnchor) { 67506 if(this.isSelected(oAnchor.record)) { 67507 // Select all rows between anchor row and target row, including target row 67508 if(oAnchor.recordIndex < nTargetRecordIndex) { 67509 for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) { 67510 if(!this.isSelected(i)) { 67511 this.selectRow(i); 67512 } 67513 } 67514 } 67515 // Select all rows between target row and anchor row, including target row 67516 else { 67517 for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) { 67518 if(!this.isSelected(i)) { 67519 this.selectRow(i); 67520 } 67521 } 67522 } 67523 } 67524 else { 67525 // Unselect all rows between anchor row and target row 67526 if(oAnchor.recordIndex < nTargetRecordIndex) { 67527 for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) { 67528 if(this.isSelected(i)) { 67529 this.unselectRow(i); 67530 } 67531 } 67532 } 67533 // Unselect all rows between target row and anchor row 67534 else { 67535 for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) { 67536 if(this.isSelected(i)) { 67537 this.unselectRow(i); 67538 } 67539 } 67540 } 67541 // Select the target row 67542 this.selectRow(oTargetRecord); 67543 } 67544 } 67545 // Invalid anchor 67546 else { 67547 // Set anchor 67548 this._oAnchorRecord = oTargetRecord; 67549 67550 // Toggle selection of target 67551 if(this.isSelected(oTargetRecord)) { 67552 this.unselectRow(oTargetRecord); 67553 } 67554 else { 67555 this.selectRow(oTargetRecord); 67556 } 67557 } 67558 } 67559 // Only SHIFT 67560 else if(bSHIFT) { 67561 this.unselectAllRows(); 67562 67563 // Validate anchor 67564 if(oAnchor) { 67565 // Select all rows between anchor row and target row,
67566 // including the anchor row and target row 67567 if(oAnchor.recordIndex < nTargetRecordIndex) { 67568 for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) { 67569 this.selectRow(i); 67570 } 67571 } 67572 // Select all rows between target row and anchor row, 67573 // including the target row and anchor row 67574 else { 67575 for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) { 67576 this.selectRow(i); 67577 } 67578 } 67579 } 67580 // Invalid anchor 67581 else { 67582 // Set anchor 67583 this._oAnchorRecord = oTargetRecord; 67584 67585 // Select target row only 67586 this.selectRow(oTargetRecord); 67587 } 67588 } 67589 // Only CTRL 67590 else if(bCTRL) { 67591 // Set anchor 67592 this._oAnchorRecord = oTargetRecord; 67593 67594 // Toggle selection of target 67595 if(this.isSelected(oTargetRecord)) { 67596 this.unselectRow(oTargetRecord); 67597 } 67598 else { 67599 this.selectRow(oTargetRecord); 67600 } 67601 } 67602 // Neither SHIFT nor CTRL 67603 else { 67604 this._handleSingleSelectionByMouse(oArgs); 67605 return; 67606 } 67607 } 67608}, 67609 67610/** 67611 * Determines selection behavior resulting from a key event when selection mode 67612 * is set to "standard". 67613 * 67614 * @method _handleStandardSelectionByKey 67615 * @param e {HTMLEvent} Event object. 67616 * @private 67617 */ 67618_handleStandardSelectionByKey : function(e) { 67619 var nKey = Ev.getCharCode(e); 67620 67621 if((nKey == 38) || (nKey == 40)) { 67622 var bSHIFT = e.shiftKey; 67623 67624 // Validate trigger 67625 var oTrigger = this._getSelectionTrigger(); 67626 // Arrow selection only works if last selected row is on current page 67627 if(!oTrigger) { 67628 return null; 67629 } 67630 67631 Ev.stopEvent(e); 67632 67633 // Validate anchor 67634 var oAnchor = this._getSelectionAnchor(oTrigger); 67635 67636 // Determine which direction we're going to 67637 if(bSHIFT) { 67638 // Selecting down away from anchor row 67639 if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) { 67640 this.selectRow(this.getNextTrEl(oTrigger.el)); 67641 } 67642 // Selecting up away from anchor row 67643 else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) { 67644 this.selectRow(this.getPreviousTrEl(oTrigger.el)); 67645 } 67646 // Unselect trigger 67647 else { 67648 this.unselectRow(oTrigger.el); 67649 } 67650 } 67651 else { 67652 this._handleSingleSelectionByKey(e); 67653 } 67654 } 67655}, 67656 67657/** 67658 * Determines selection behavior resulting from a mouse event when selection mode 67659 * is set to "single". 67660 * 67661 * @method _handleSingleSelectionByMouse 67662 * @param oArgs.event {HTMLEvent} Event object. 67663 * @param oArgs.target {HTMLElement} Target element. 67664 * @private 67665 */ 67666_handleSingleSelectionByMouse : function(oArgs) { 67667 var elTarget = oArgs.target; 67668 67669 // Validate target row 67670 var elTargetRow = this.getTrEl(elTarget); 67671 if(elTargetRow) { 67672 var oTargetRecord = this.getRecord(elTargetRow); 67673 67674 // Set anchor 67675 this._oAnchorRecord = oTargetRecord; 67676 67677 // Select only target 67678 this.unselectAllRows(); 67679 this.selectRow(oTargetRecord); 67680 } 67681}, 67682 67683/** 67684 * Determines selection behavior resulting from a key event when selection mode 67685 * is set to "single". 67686 * 67687 * @method _handleSingleSelectionByKey 67688 * @param e {HTMLEvent} Event object. 67689 * @private 67690 */ 67691_handleSingleSelectionByKey : function(e) { 67692 var nKey = Ev.getCharCode(e); 67693 67694 if((nKey == 38) || (nKey == 40)) { 67695 // Validate trigger 67696 var oTrigger = this._getSelectionTrigger(); 67697 // Arrow selection only works if last selected row is on current page 67698 if(!oTrigger) { 67699 return null; 67700 } 67701 67702 Ev.stopEvent(e); 67703 67704 // Determine the new row to select 67705 var elNew; 67706 if(nKey == 38) { // arrow up 67707 elNew = this.getPreviousTrEl(oTrigger.el); 67708 67709 // Validate new row 67710 if(elNew === null) { 67711 //TODO: wrap around to last tr on current page 67712 //elNew = this.getLastTrEl(); 67713 67714 //TODO: wrap back to last tr of previous page 67715 67716 // Top row selection is sticky 67717 elNew = this.getFirstTrEl(); 67718 } 67719 } 67720 else if(nKey == 40) { // arrow down 67721 elNew = this.getNextTrEl(oTrigger.el); 67722 67723 // Validate new row 67724 if(elNew === null) { 67725 //TODO: wrap around to first tr on current page 67726 //elNew = this.getFirstTrEl(); 67727 67728 //TODO: wrap forward to first tr of previous page 67729 67730 // Bottom row selection is sticky 67731 elNew = this.getLastTrEl(); 67732 } 67733 } 67734 67735 // Unselect all rows 67736 this.unselectAllRows(); 67737 67738 // Select the new row 67739 this.selectRow(elNew); 67740 67741 // Set new anchor 67742 this._oAnchorRecord = this.getRecord(elNew); 67743 } 67744}, 67745 67746/** 67747 * Determines selection behavior resulting from a mouse event when selection mode 67748 * is set to "cellblock". 67749 * 67750 * @method _handleCellBlockSelectionByMouse 67751 * @param oArgs.event {HTMLEvent} Event object. 67752 * @param oArgs.target {HTMLElement} Target element. 67753 * @private 67754 */ 67755_handleCellBlockSelectionByMouse : function(oArgs) { 67756 var elTarget = oArgs.target; 67757 67758 // Validate target cell 67759 var elTargetCell = this.getTdEl(elTarget); 67760 if(elTargetCell) { 67761 var e = oArgs.event; 67762 var bSHIFT = e.shiftKey; 67763 var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey); 67764 67765 var elTargetRow = this.getTrEl(elTargetCell); 67766 var nTargetTrIndex = this.getTrIndex(elTargetRow); 67767 var oTargetColumn = this.getColumn(elTargetCell); 67768 var nTargetColKeyIndex = oTargetColumn.getKeyIndex(); 67769 var oTargetRecord = this.getRecord(elTargetRow); 67770 var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord); 67771 var oTargetCell = {record:oTargetRecord, column:oTargetColumn}; 67772 67773 var oAnchor = this._getSelectionAnchor(); 67774 67775 var allRows = this.getTbodyEl().rows; 67776 var startIndex, endIndex, currentRow, i, j; 67777 67778 // Both SHIFT and CTRL 67779 if(bSHIFT && bCTRL) { 67780 67781 // Validate anchor 67782 if(oAnchor) { 67783 // Anchor is selected 67784 if(this.isSelected(oAnchor.cell)) { 67785 // All cells are on the same row 67786 if(oAnchor.recordIndex === nTargetRecordIndex) { 67787 // Select all cells between anchor cell and target cell, including target cell 67788 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 67789 for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) { 67790 this.selectCell(elTargetRow.cells[i]); 67791 } 67792 } 67793 // Select all cells between target cell and anchor cell, including target cell 67794 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 67795 for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) { 67796 this.selectCell(elTargetRow.cells[i]); 67797 } 67798 } 67799 } 67800 // Anchor row is above target row 67801 else if(oAnchor.recordIndex < nTargetRecordIndex) { 67802 startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex); 67803 endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex); 67804 67805 // Select all cells from startIndex to endIndex on rows between anchor row and target row 67806 for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) { 67807 for(j=startIndex;
67807 j<=endIndex; j++) { 67808 this.selectCell(allRows[i].cells[j]); 67809 } 67810 } 67811 } 67812 // Anchor row is below target row 67813 else { 67814 startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex); 67815 endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex); 67816 67817 // Select all cells from startIndex to endIndex on rows between target row and anchor row 67818 for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) { 67819 for(j=endIndex; j>=startIndex; j--) { 67820 this.selectCell(allRows[i].cells[j]); 67821 } 67822 } 67823 } 67824 } 67825 // Anchor cell is unselected 67826 else { 67827 // All cells are on the same row 67828 if(oAnchor.recordIndex === nTargetRecordIndex) { 67829 // Unselect all cells between anchor cell and target cell 67830 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 67831 for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) { 67832 this.unselectCell(elTargetRow.cells[i]); 67833 } 67834 } 67835 // Select all cells between target cell and anchor cell 67836 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 67837 for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) { 67838 this.unselectCell(elTargetRow.cells[i]); 67839 } 67840 } 67841 } 67842 // Anchor row is above target row 67843 if(oAnchor.recordIndex < nTargetRecordIndex) { 67844 // Unselect all cells from anchor cell to target cell 67845 for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) { 67846 currentRow = allRows[i]; 67847 for(j=0; j<currentRow.cells.length; j++) { 67848 // This is the anchor row, only unselect cells after the anch
67848or cell 67849 if(currentRow.sectionRowIndex === oAnchor.trIndex) { 67850 if(j>oAnchor.colKeyIndex) { 67851 this.unselectCell(currentRow.cells[j]); 67852 } 67853 } 67854 // This is the target row, only unelect cells before the target cell 67855 else if(currentRow.sectionRowIndex === nTargetTrIndex) { 67856 if(j<nTargetColKeyIndex) { 67857 this.unselectCell(currentRow.cells[j]); 67858 } 67859 } 67860 // Unselect all cells on this row 67861 else { 67862 this.unselectCell(currentRow.cells[j]); 67863 } 67864 } 67865 } 67866 } 67867 // Anchor row is below target row 67868 else { 67869 // Unselect all cells from target cell to anchor cell 67870 for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) { 67871 currentRow = allRows[i]; 67872 for(j=0; j<currentRow.cells.length; j++) { 67873 // This is the target row, only unselect cells after the target cell 67874 if(currentRow.sectionRowIndex == nTargetTrIndex) { 67875 if(j>nTargetColKeyIndex) { 67876 this.unselectCell(currentRow.cells[j]); 67877 } 67878 } 67879 // This is the anchor row, only unselect cells before the anchor cell 67880 else if(currentRow.sectionRowIndex == oAnchor.trIndex) { 67881 if(j<oAnchor.colKeyIndex) { 67882 this.unselectCell(currentRow.cells[j]); 67883 } 67884 } 67885 // Unselect all cells on this row 67886 else { 67887 this.unselectCell(currentRow.cells[j]); 67888 } 67889 } 67890 } 67891 } 67892 67893 // Select the target cell 67894 this.selectCell(elTargetCell); 67895 } 67896 } 67897 // Invalid anchor 67898 else { 67899 // Set anchor 67900 this._oAnchorCell = oTargetCell; 67901 67902 // Toggle selection of target 67903 if(this.isSelected(oTargetCell)) { 67904 this.unselectCell(oTargetCell); 67905 } 67906 else { 67907 this.selectCell(oTargetCell); 67908 } 67909 } 67910 67911 } 67912 // Only SHIFT 67913 else if(bSHIFT) { 67914 this.unselectAllCells(); 67915 67916 // Validate anchor 67917 if(oAnchor) { 67918 // All cells are on the same row 67919 if(oAnchor.recordIndex === nTargetRecordIndex) { 67920 // Select all cells between anchor cell and target cell, 67921 // including the anchor cell and target cell 67922 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 67923 for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) { 67924 this.selectCell(elTargetRow.cells[i]); 67925 } 67926 } 67927 // Select all cells between target cell and anchor cell 67928 // including the target cell and anchor cell 67929 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 67930 for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) { 67931 this.selectCell(elTargetRow.cells[i]); 67932 } 67933 } 67934 } 67935 // Anchor row is above target row 67936 else if(oAnchor.recordIndex < nTargetRecordIndex) {
67937 // Select the cellblock from anchor cell to target cell 67938 // including the anchor cell and the target cell 67939 startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex); 67940 endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex); 67941 67942 for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) { 67943 for(j=startIndex; j<=endIndex; j++) { 67944 this.selectCell(allRows[i].cells[j]); 67945 } 67946 } 67947 } 67948 // Anchor row is below target row 67949 else { 67950 // Select the cellblock from target cell to anchor cell 67951 // including the target cell and the anchor cell 67952 startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex); 67953 endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex); 67954 67955 for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) { 67956 for(j=startIndex;
67956 j<=endIndex; j++) { 67957 this.selectCell(allRows[i].cells[j]); 67958 } 67959 } 67960 } 67961 } 67962 // Invalid anchor 67963 else { 67964 // Set anchor 67965 this._oAnchorCell = oTargetCell; 67966 67967 // Select target only 67968 this.selectCell(oTargetCell); 67969 } 67970 } 67971 // Only CTRL 67972 else if(bCTRL) { 67973 67974 // Set anchor 67975 this._oAnchorCell = oTargetCell; 67976 67977 // Toggle selection of target 67978 if(this.isSelected(oTargetCell)) { 67979 this.unselectCell(oTargetCell); 67980 } 67981 else { 67982 this.selectCell(oTargetCell); 67983 } 67984 67985 } 67986 // Neither SHIFT nor CTRL 67987 else { 67988 this._handleSingleCellSelectionByMouse(oArgs); 67989 } 67990 } 67991}, 67992 67993/** 67994 * Determines selection behavior resulting from a key event when selection mode 67995 * is set to "cellblock". 67996 * 67997 * @method _handleCellBlockSelectionByKey 67998 * @param e {HTMLEvent} Event object. 67999 * @private 68000 */ 68001_handleCellBlockSelectionByKey : function(e) { 68002 var nKey = Ev.getCharCode(e); 68003 var bSHIFT = e.shiftKey; 68004 if((nKey == 9) || !bSHIFT) { 68005 this._handleSingleCellSelectionByKey(e); 68006 return; 68007 } 68008 68009 if((nKey > 36) && (nKey < 41)) { 68010 // Validate trigger 68011 var oTrigger = this._getSelectionTrigger(); 68012 // Arrow selection only works if last selected row is on current page 68013 if(!oTrigger) { 68014 return null; 68015 } 68016 68017 Ev.stopEvent(e); 68018 68019 // Validate anchor 68020 var oAnchor = this._getSelectionAnchor(oTrigger); 68021 68022 var i, startIndex, endIndex, elNew, elNewRow; 68023 var allRows = this.getTbodyEl().rows; 68024 var elThisRow = oTrigger.el.parentNode; 68025 68026 // Determine which direction we're going to 68027 68028 if(nKey == 40) { // arrow down 68029 // Selecting away from anchor cell 68030 if(oAnchor.recordIndex <= oTrigger.recordIndex) { 68031 // Select the horiz block on the next row... 68032 // ...making sure there is room below the trigger row 68033 elNewRow = this.getNextTrEl(oTrigger.el); 68034 if(elNewRow) { 68035 startIndex = oAnchor.colKeyIndex; 68036 endIndex = oTrigger.colKeyIndex; 68037 // ...going left 68038 if(startIndex > endIndex) { 68039 for(i=startIndex; i>=endIndex; i--) { 68040 elNew = elNewRow.cells[i]; 68041 this.selectCell(elNew); 68042 } 68043 } 68044 // ... going right 68045 else { 68046 for(i=startIndex; i<=endIndex; i++) { 68047 elNew = elNewRow.cells[i]; 68048 this.selectCell(elNew); 68049 } 68050 } 68051 } 68052 } 68053 // Unselecting towards anchor cell 68054 else { 68055 startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex); 68056 endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex); 68057 // Unselect the horiz block on this row towards the next row 68058 for(i=startIndex; i<=endIndex; i++) { 68059 this.unselectCell(elThisRow.cells[i]); 68060 } 68061 } 68062 } 68063 // Arrow up 68064 else if(nKey == 38) { 68065 // Selecting away from anchor cell 68066 if(oAnchor.recordIndex >= oTrigger.recordIndex) { 68067 // Select the horiz block on the previous row... 68068 // ...making sure there is room 68069 elNewRow = this.getPreviousTrEl(oTrigger.el); 68070 if(elNewRow) { 68071 // Select in order from anchor to trigger... 68072 startIndex = oAnchor.colKeyIndex; 68073 endIndex = oTrigger.colKeyIndex; 68074 // ...going left 68075 if(startIndex > endIndex) { 68076 for(i=startIndex; i>=endIndex; i--) { 68077 elNew = elNewRow.cells[i]; 68078 this.selectCell(elNew); 68079 } 68080 } 68081 // ... going right 68082 else { 68083 for(i=startIndex; i<=endIndex; i++) { 68084 elNew = elNewRow.cells[i]; 68085 this.selectCell(elNew); 68086 } 68087 } 68088 } 68089 } 68090 // Unselecting towards anchor cell 68091 else { 68092 startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex); 68093 endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex); 68094 // Unselect the horiz block on this row towards the previous row 68095 for(i=startIndex; i<=endIndex; i++) { 68096 this.unselectCell(elThisRow.cells[i]); 68097 } 68098 } 68099 } 68100 // Arrow right 68101 else if(nKey == 39) {
68102 // Selecting away from anchor cell 68103 if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) { 68104 // Select the next vert block to the right... 68105 // ...making sure there is room 68106 if(oTrigger.colKeyIndex < elThisRow.cells.length-1) { 68107 // Select in order from anchor to trigger... 68108 startIndex = oAnchor.trIndex; 68109 endIndex = oTrigger.trIndex; 68110 // ...going up 68111 if(startIndex > endIndex) { 68112 for(i=startIndex; i>=endIndex; i--) { 68113 elNew = allRows[i].cells[oTrigger.colKeyIndex+1]; 68114 this.selectCell(elNew); 68115 } 68116 } 68117 // ... going down 68118 else { 68119 for(i=startIndex; i<=endIndex; i++) { 68120 elNew = allRows[i].cells[oTrigger.colKeyIndex+1]; 68121 this.selectCell(elNew); 68122 } 68123 } 68124 } 68125 } 68126 // Unselecting towards anchor cell 68127 else { 68128 // Unselect the vert block on this column towards the right 68129 startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex); 68130 endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex); 68131 for(i=startIndex; i<=endIndex; i++) { 68132 this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]); 68133 } 68134 } 68135 } 68136 // Arrow left 68137 else if(nKey == 37) { 68138 // Selecting away from anchor cell 68139 if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) { 68140 //Select the previous vert block to the left 68141 if(oTrigger.colKeyIndex > 0) { 68142 // Select in order from anchor to trigger... 68143 startIndex = oAnchor.trIndex; 68144 endIndex = oTrigger.trIndex; 68145 // ...going up 68146 if(startIndex > endIndex) { 68147 for(i=startIndex; i>=endIndex; i--) { 68148 elNew = allRows[i].cells[oTrigger.colKeyIndex-1]; 68149 this.selectCell(elNew); 68150 } 68151 } 68152 // ... going down 68153 else { 68154 for(i=startIndex; i<=endIndex; i++) { 68155 elNew = allRows[i].cells[oTrigger.colKeyIndex-1]; 68156 this.selectCell(elNew); 68157 } 68158 } 68159 } 68160 } 68161 // Unselecting towards anchor cell 68162 else { 68163 // Unselect the vert block on this column towards the left 68164 startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex); 68165 endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex); 68166 for(i=startIndex; i<=endIndex; i++) { 68167 this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]); 68168 } 68169 } 68170 } 68171 } 68172}, 68173 68174/** 68175 * Determines selection behavior resulting from a mouse event when selection mode 68176 * is set to "cellrange". 68177 * 68178 * @method _handleCellRangeSelectionByMouse 68179 * @param oArgs.event {HTMLEvent} Event object. 68180 * @param oArgs.target {HTMLElement} Target element. 68181 * @private 68182 */ 68183_handleCellRangeSelectionByMouse : function(oArgs) { 68184 var elTarget = oArgs.target; 68185 68186 // Validate target cell 68187 var elTargetCell = this.getTdEl(elTarget); 68188 if(elTargetCell) { 68189 var e = oArgs.event; 68190 var bSHIFT = e.shiftKey; 68191 var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey); 68192 68193 var elTargetRow = this.getTrEl(elTargetCell); 68194 var nTargetTrIndex = this.getTrIndex(elTargetRow); 68195 var oTargetColumn = this.getColumn(elTargetCell); 68196 var nTargetColKeyIndex = oTargetColumn.getKeyIndex(); 68197 var oTargetRecord = this.getRecord(elTargetRow); 68198 var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord); 68199 var oTargetCell = {record:oTargetRecord, column:oTargetColumn}; 68200 68201 var oAnchor = this._getSelectionAnchor(); 68202 68203 var allRows = this.getTbodyEl().rows; 68204 var currentRow, i, j; 68205 68206 // Both SHIFT and CTRL 68207 if(bSHIFT && bCTRL) { 68208 68209 // Validate anchor 68210 if(oAnchor) { 68211 // Anchor is selected 68212 if(this.isSelected(oAnchor.cell)) { 68213 // All cells are on the same row 68214 if(oAnchor.recordIndex === nTargetRecordIndex) { 68215 // Select all cells between anchor cell and target cell, including target cell 68216 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 68217 for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) { 68218 this.selectCell(elTargetRow.cells[i]); 68219 } 68220 } 68221 // Select all cells between target cell and anchor cell, including target cell 68222 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 68223 for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) { 68224 this.selectCell(elTargetRow.cells[i]); 68225 } 68226 } 68227 } 68228 // Anchor row is above target row 68229 else if(oAnchor.recordIndex < nTargetRecordIndex) { 68230 // Select all cells on anchor row from anch
68230or cell to the end of the row 68231 for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) { 68232 this.selectCell(elTargetRow.cells[i]); 68233 } 68234 68235 // Select all cells on all rows between anchor row and target row 68236 for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) { 68237 for(j=0; j<allRows[i].cells.length; j++){ 68238 this.selectCell(allRows[i].cells[j]); 68239 } 68240 } 68241 68242 // Select all cells on target row from first cell to the target cell 68243 for(i=0; i<=nTargetColKeyIndex; i++) { 68244 this.selectCell(elTargetRow.cells[i]); 68245 } 68246 } 68247 // Anchor row is below target row 68248 else { 68249 // Select all cells on target row from target cell to the end of the row 68250 for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) { 68251 this.selectCell(elTargetRow.cells[i]); 68252 } 68253 68254 // Select all cells on all rows between target row and anchor row 68255 for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) { 68256 for(j=0; j<allRows[i].cells.length; j++){ 68257 this.selectCell(allRows[i].cells[j]); 68258 } 68259 } 68260 68261 // Select all cells on anchor row from first cell to the anchor cell 68262 for(i=0; i<oAnchor.colKeyIndex; i++) { 68263 this.selectCell(elTargetRow.cells[i]); 68264 } 68265 } 68266 } 68267 // Anchor cell is unselected 68268 else { 68269 // All cells are on the same row 68270 if(oAnchor.recordIndex === nTargetRecordIndex) { 68271 // Unselect all cells between anchor cell and target cell 68272 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 68273 for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) { 68274 this.unselectCell(elTargetRow.cells[i]); 68275 } 68276 } 68277 // Select all cells between target cell and anchor cell 68278 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 68279 for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) { 68280 this.unselectCell(elTargetRow.cells[i]); 68281 } 68282 } 68283 } 68284 // Anchor row is above target row 68285 if(oAnchor.recordIndex < nTargetRecordIndex) { 68286 // Unselect all cells from anchor cell to target cell 68287 for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) { 68288 currentRow = allRows[i]; 68289 for(j=0; j<currentRow.cells.length; j++) { 68290 // This is the anchor row, only unselect cells after the anch
68290or cell 68291 if(currentRow.sectionRowIndex === oAnchor.trIndex) { 68292 if(j>oAnchor.colKeyIndex) { 68293 this.unselectCell(currentRow.cells[j]); 68294 } 68295 } 68296 // This is the target row, only unelect cells before the target cell 68297 else if(currentRow.sectionRowIndex === nTargetTrIndex) { 68298 if(j<nTargetColKeyIndex) { 68299 this.unselectCell(currentRow.cells[j]); 68300 } 68301 } 68302 // Unselect all cells on this row 68303 else { 68304 this.unselectCell(currentRow.cells[j]); 68305 } 68306 } 68307 } 68308 } 68309 // Anchor row is below target row 68310 else { 68311 // Unselect all cells from target cell to anchor cell 68312 for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) { 68313 currentRow = allRows[i]; 68314 for(j=0; j<currentRow.cells.length; j++) { 68315 // This is the target row, only unselect cells after the target cell 68316 if(currentRow.sectionRowIndex == nTargetTrIndex) { 68317 if(j>nTargetColKeyIndex) { 68318 this.unselectCell(currentRow.cells[j]); 68319 } 68320 } 68321 // This is the anchor row, only unselect cells before the anchor cell 68322 else if(currentRow.sectionRowIndex == oAnchor.trIndex) { 68323 if(j<oAnchor.colKeyIndex) { 68324 this.unselectCell(currentRow.cells[j]); 68325 } 68326 } 68327 // Unselect all cells on this row 68328 else { 68329 this.unselectCell(currentRow.cells[j]); 68330 } 68331 } 68332 } 68333 } 68334 68335 // Select the target cell 68336 this.selectCell(elTargetCell); 68337 } 68338 } 68339 // Invalid anchor 68340 else { 68341 // Set anchor 68342 this._oAnchorCell = oTargetCell; 68343 68344 // Toggle selection of target 68345 if(this.isSelected(oTargetCell)) { 68346 this.unselectCell(oTargetCell); 68347 } 68348 else { 68349 this.selectCell(oTargetCell); 68350 } 68351 } 68352 } 68353 // Only SHIFT 68354 else if(bSHIFT) { 68355 68356 this.unselectAllCells(); 68357 68358 // Validate anchor 68359 if(oAnchor) { 68360 // All cells are on the same row 68361 if(oAnchor.recordIndex === nTargetRecordIndex) { 68362 // Select all cells between anchor cell and target cell, 68363 // including the anchor cell and target cell 68364 if(oAnchor.colKeyIndex < nTargetColKeyIndex) { 68365 for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) { 68366 this.selectCell(elTargetRow.cells[i]); 68367 } 68368 } 68369 // Select all cells between target cell and anchor cell 68370 // including the target cell and anchor cell 68371 else if(nTargetColKeyIndex < oAnchor.colKeyIndex) { 68372 for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) { 68373 this.selectCell(elTargetRow.cells[i]); 68374 } 68375 } 68376 } 68377 // Anchor row is above target row 68378 else if(oAnchor.recordIndex < nTargetRecordIndex) {
68379 // Select all cells from anchor cell to target cell 68380 // including the anchor cell and target cell 68381 for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) { 68382 currentRow = allRows[i]; 68383 for(j=0; j<currentRow.cells.length; j++) { 68384 // This is the anchor row, only select the anchor cell and after 68385 if(currentRow.sectionRowIndex == oAnchor.trIndex) { 68386 if(j>=oAnchor.colKeyIndex) { 68387 this.selectCell(currentRow.cells[j]); 68388 } 68389 } 68390 // This is the target row, only select the target cell and before 68391 else if(currentRow.sectionRowIndex == nTargetTrIndex) { 68392 if(j<=nTargetColKeyIndex) { 68393 this.selectCell(currentRow.cells[j]); 68394 } 68395 } 68396 // Select all cells on this row 68397 else { 68398 this.selectCell(currentRow.cells[j]); 68399 } 68400 } 68401 } 68402 } 68403 // Anchor row is below target row 68404 else { 68405 // Select all cells from target cell to anchor cell, 68406 // including the target cell and anchor cell 68407 for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) { 68408 currentRow = allRows[i]; 68409 for(j=0; j<currentRow.cells.length; j++) { 68410 // This is the target row, only select the target cell and after 68411 if(currentRow.sectionRowIndex == nTargetTrIndex) { 68412 if(j>=nTargetColKeyIndex) { 68413 this.selectCell(currentRow.cells[j]); 68414 } 68415 } 68416 // This is the anchor row, only select the anchor cell and before 68417 else if(currentRow.sectionRowIndex == oAnchor.trIndex) { 68418 if(j<=oAnchor.colKeyIndex) { 68419 this.selectCell(currentRow.cells[j]); 68420 } 68421 } 68422 // Select all cells on this row 68423 else { 68424 this.selectCell(currentRow.cells[j]); 68425 } 68426 } 68427 } 68428 } 68429 } 68430 // Invalid anchor 68431 else { 68432 // Set anchor 68433 this._oAnchorCell = oTargetCell; 68434 68435 // Select target only 68436 this.selectCell(oTargetCell); 68437 } 68438 68439 68440 } 68441 // Only CTRL 68442 else if(bCTRL) { 68443 68444 // Set anchor 68445 this._oAnchorCell = oTargetCell; 68446 68447 // Toggle selection of target 68448 if(this.isSelected(oTargetCell)) { 68449 this.unselectCell(oTargetCell); 68450 } 68451 else { 68452 this.selectCell(oTargetCell); 68453 } 68454 68455 } 68456 // Neither SHIFT nor CTRL 68457 else { 68458 this._handleSingleCellSelectionByMouse(oArgs); 68459 } 68460 } 68461}, 68462 68463/** 68464 * Determines selection behavior resulting from a key event when selection mode 68465 * is set to "cellrange". 68466 * 68467 * @method _handleCellRangeSelectionByKey 68468 * @param e {HTMLEvent} Event object. 68469 * @private 68470 */ 68471_handleCellRangeSelectionByKey : function(e) { 68472 var nKey = Ev.getCharCode(e); 68473 var bSHIFT = e.shiftKey; 68474 if((nKey == 9) || !bSHIFT) { 68475 this._handleSingleCellSelectionByKey(e); 68476 return; 68477 } 68478 68479 if((nKey > 36) && (nKey < 41)) { 68480 // Validate trigger 68481 var oTrigger = this._getSelectionTrigger(); 68482 // Arrow selection only works if last selected row is on current page 68483 if(!oTrigger) { 68484 return null; 68485 } 68486 68487 Ev.stopEvent(e); 68488 68489 // Validate anchor 68490 var oAnchor = this._getSelectionAnchor(oTrigger); 68491 68492 var i, elNewRow, elNew; 68493 var allRows = this.getTbodyEl().rows; 68494 var elThisRow = oTrigger.el.parentNode; 68495 68496 // Arrow down 68497 if(nKey == 40) { 68498 elNewRow = this.getNextTrEl(oTrigger.el); 68499
68500 // Selecting away from anchor cell 68501 if(oAnchor.recordIndex <= oTrigger.recordIndex) { 68502 // Select all cells to the end of this row 68503 for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){ 68504 elNew = elThisRow.cells[i]; 68505 this.selectCell(elNew); 68506 } 68507 68508 // Select some of the cells on the next row down 68509 if(elNewRow) { 68510 for(i=0; i<=oTrigger.colKeyIndex; i++){ 68511 elNew = elNewRow.cells[i]; 68512 this.selectCell(elNew); 68513 } 68514 } 68515 } 68516 // Unselecting towards anchor cell 68517 else { 68518 // Unselect all cells to the end of this row 68519 for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){ 68520 this.unselectCell(elThisRow.cells[i]); 68521 } 68522 68523 // Unselect some of the cells on the next row down 68524 if(elNewRow) { 68525 for(i=0; i<oTrigger.colKeyIndex; i++){ 68526 this.unselectCell(elNewRow.cells[i]); 68527 } 68528 } 68529 } 68530 } 68531 // Arrow up 68532 else if(nKey == 38) { 68533 elNewRow = this.getPreviousTrEl(oTrigger.el); 68534 68535 // Selecting away from anchor cell 68536 if(oAnchor.recordIndex >= oTrigger.recordIndex) { 68537 // Select all the cells to the beginning of this row 68538 for(i=oTrigger.colKeyIndex-1; i>-1; i--){ 68539 elNew = elThisRow.cells[i]; 68540 this.selectCell(elNew); 68541 } 68542 68543 // Select some of the cells from the end of the previous row 68544 if(elNewRow) { 68545 for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){ 68546 elNew = elNewRow.cells[i]; 68547 this.selectCell(elNew); 68548 } 68549 } 68550 } 68551 // Unselecting towards anchor cell 68552 else { 68553 // Unselect all the cells to the beginning of this row 68554 for(i=oTrigger.colKeyIndex; i>-1; i--){ 68555 this.unselectCell(elThisRow.cells[i]); 68556 } 68557 68558 // Unselect some of the cells from the end of the previous row 68559 if(elNewRow) { 68560 for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){ 68561 this.unselectCell(elNewRow.cells[i]); 68562 } 68563 } 68564 } 68565 } 68566 // Arrow right 68567 else if(nKey == 39) { 68568 elNewRow = this.getNextTrEl(oTrigger.el); 68569 68570 // Selecting away from anchor cell 68571 if(oAnchor.recordIndex < oTrigger.recordIndex) { 68572 // Select the next cell to the right 68573 if(oTrigger.colKeyIndex < elThisRow.cells.length-1) { 68574 elNew = elThisRow.cells[oTrigger.colKeyIndex+1]; 68575 this.selectCell(elNew); 68576 } 68577 // Select the first cell of the next row 68578 else if(elNewRow) { 68579 elNew = elNewRow.cells[0]; 68580 this.selectCell(elNew); 68581 } 68582 } 68583 // Unselecting towards anchor cell 68584 else if(oAnchor.recordIndex > oTrigger.recordIndex) { 68585 this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]); 68586 68587 // Unselect this cell towards the right 68588 if(oTrigger.colKeyIndex < elThisRow.cells.length-1) { 68589 } 68590 // Unselect this cells towards the first cell of the next row 68591 else { 68592 } 68593 } 68594 // Anchor is on this row 68595 else { 68596 // Selecting away from anchor 68597 if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) { 68598 // Select the next cell to the right 68599 if(oTrigger.colKeyIndex < elThisRow.cells.length-1) { 68600 elNew = elThisRow.cells[oTrigger.colKeyIndex+1]; 68601 this.selectCell(elNew); 68602 } 68603 // Select the first cell on the next row 68604 else if(oTrigger.trIndex < allRows.length-1){ 68605 elNew = elNewRow.cells[0]; 68606 this.selectCell(elNew); 68607 } 68608 } 68609 // Unselecting towards anchor 68610 else { 68611 // Unselect this cell towards the right 68612 this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]); 68613 } 68614 } 68615 } 68616 // Arrow left 68617 else if(nKey == 37) { 68618 elNewRow = this.getPreviousTrEl(oTrigger.el); 68619
68620 // Unselecting towards the anchor 68621 if(oAnchor.recordIndex < oTrigger.recordIndex) { 68622 this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]); 68623 68624 // Unselect this cell towards the left 68625 if(oTrigger.colKeyIndex > 0) { 68626 } 68627 // Unselect this cell towards the last cell of the previous row 68628 else { 68629 } 68630 } 68631 // Selecting towards the anchor 68632 else if(oAnchor.recordIndex > oTrigger.recordIndex) { 68633 // Select the next cell to the left 68634 if(oTrigger.colKeyIndex > 0) { 68635 elNew = elThisRow.cells[oTrigger.colKeyIndex-1]; 68636 this.selectCell(elNew); 68637 } 68638 // Select the last cell of the previous row 68639 else if(oTrigger.trIndex > 0){ 68640 elNew = elNewRow.cells[elNewRow.cells.length-1]; 68641 this.selectCell(elNew); 68642 } 68643 } 68644 // Anchor is on this row 68645 else { 68646 // Selecting away from anchor cell 68647 if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) { 68648 // Select the next cell to the left 68649 if(oTrigger.colKeyIndex > 0) { 68650 elNew = elThisRow.cells[oTrigger.colKeyIndex-1]; 68651 this.selectCell(elNew); 68652 } 68653 // Select the last cell of the previous row 68654 else if(oTrigger.trIndex > 0){ 68655 elNew = elNewRow.cells[elNewRow.cells.length-1]; 68656 this.selectCell(elNew); 68657 } 68658 } 68659 // Unselecting towards anchor cell 68660 else { 68661 this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]); 68662 68663 // Unselect this cell towards the left 68664 if(oTrigger.colKeyIndex > 0) { 68665 } 68666 // Unselect this cell towards the last cell of the previous row 68667 else { 68668 } 68669 } 68670 } 68671 } 68672 } 68673}, 68674 68675/** 68676 * Determines selection behavior resulting from a mouse event when selection mode 68677 * is set to "singlecell". 68678 * 68679 * @method _handleSingleCellSelectionByMouse 68680 * @param oArgs.event {HTMLEvent} Event object. 68681 * @param oArgs.target {HTMLElement} Target element. 68682 * @private 68683 */ 68684_handleSingleCellSelectionByMouse : function(oArgs) { 68685 var elTarget = oArgs.target; 68686 68687 // Validate target cell 68688 var elTargetCell = this.getTdEl(elTarget); 68689 if(elTargetCell) { 68690 var elTargetRow = this.getTrEl(elTargetCell); 68691 var oTargetRecord = this.getRecord(elTargetRow); 68692 var oTargetColumn = this.getColumn(elTargetCell); 68693 var oTargetCell = {record:oTargetRecord, column:oTargetColumn}; 68694 68695 // Set anchor 68696 this._oAnchorCell = oTargetCell; 68697 68698 // Select only target 68699 this.unselectAllCells(); 68700 this.selectCell(oTargetCell); 68701 } 68702}, 68703 68704/** 68705 * Determines selection behavior resulting from a key event when selection mode 68706 * is set to "singlecell". 68707 * 68708 * @method _handleSingleCellSelectionByKey 68709 * @param e {HTMLEvent} Event object. 68710 * @private 68711 */ 68712_handleSingleCellSelectionByKey : function(e) { 68713 var nKey = Ev.getCharCode(e); 68714 if((nKey == 9) || ((nKey > 36) && (nKey < 41))) { 68715 var bSHIFT = e.shiftKey; 68716 68717 // Validate trigger 68718 var oTrigger = this._getSelectionTrigger(); 68719 // Arrow selection only works if last selected row is on current page 68720 if(!oTrigger) { 68721 return null; 68722 } 68723 68724 // Determine the new cell to select 68725 var elNew; 68726 if(nKey == 40) { // Arrow down 68727 elNew = this.getBelowTdEl(oTrigger.el); 68728 68729 // Validate new cell 68730 if(elNew === null) { 68731 //TODO: wrap around to first tr on current page 68732 68733 //TODO: wrap forward to first tr of next page 68734 68735 // Bottom selection is sticky 68736 elNew = oTrigger.el; 68737 } 68738 } 68739 else if(nKey == 38) { // Arrow up 68740 elNew = this.getAboveTdEl(oTrigger.el); 68741 68742 // Validate new cell 68743 if(elNew === null) { 68744 //TODO: wrap around to last tr on current page 68745 68746 //TODO: wrap back to last tr of previous page 68747 68748 // Top selection is sticky 68749 elNew = oTrigger.el; 68750 } 68751 } 68752 else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab 68753 elNew = this.getNextTdEl(oTrigger.el); 68754 68755 // Validate new cell 68756 if(elNew === null) { 68757 //TODO: wrap around to first td on current page 68758 68759 //TODO: wrap forward to first td of next page 68760 68761 // Top-left selection is sticky, and release TAB focus 68762 //elNew = oTrigger.el; 68763 return; 68764 } 68765 } 68766 else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab 68767 elNew = this.getPreviousTdEl(oTrigger.el); 68768 68769 // Validate new cell 68770 if(elNew === null) { 68771 //TODO: wrap around to last td on current page 68772 68773 //TODO: wrap back to last td of previous page 68774 68775 // Bottom-right selection is sticky, and release TAB focus 68776 //elNew = oTrigger.el; 68777 return; 68778 } 68779 } 68780 68781 Ev.stopEvent(e); 68782 68783 // Unselect all cells 68784 this.unselectAllCells(); 68785 68786 // Select the new cell 68787 this.selectCell(elNew); 68788 68789 // Set new anchor 68790 this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)}; 68791 } 68792}, 68793 68794/** 68795 * Returns array of selected TR elements on the page. 68796 * 68797 * @method getSelectedTrEls 68798 * @return {HTMLElement[]} Array of selected TR elements. 68799 */ 68800getSelectedTrEls : function() { 68801 return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody); 68802}, 68803 68804/** 68805 * Sets given row to the selected state. 68806 * 68807 * @method selectRow 68808 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element 68809 * reference or ID string, Record instance, or RecordSet position index. 68810 */ 68811selectRow : function(row) { 68812 var oRecord, elRow; 68813 68814 if(row instanceof YAHOO.widget.Record) { 68815 oRecord = this._oRecordSet.getRecord(row); 68816 elRow = this.getTrEl(oRecord); 68817 } 68818 else if(lang.isNumber(row)) { 68819 oRecord = this.getRecord(row); 68820 elRow = this.getTrEl(oRecord); 68821 } 68822 else { 68823 elRow = this.getTrEl(row); 68824 oRecord = this.getRecord(elRow); 68825 } 68826 68827 if(oRecord) { 68828 // Update selection trackers 68829 var tracker = this._aSelections || []; 68830 var sRecordId = oRecord.getId(); 68831 var index = -1; 68832 68833 // Remove if already there: 68834 // Use Array.indexOf if available... 68835 /*if(tracker.indexOf && (tracker.indexOf(sRecordId) > -1)) { 68836 tracker.splice(tracker.indexOf(sRecordId),1); 68837 }*/ 68838 if(tracker.indexOf) { 68839 index = tracker.indexOf(sRecordId); 68840 68841 } 68842 // ...or do it the old-fashioned way 68843 else { 68844 for(var j=tracker.length-1; j>-1; j--) { 68845 if(tracker[j] === sRecordId){ 68846 index = j; 68847 break; 68848 } 68849 } 68850 } 68851 if(index > -1) { 68852 tracker.splice(index,1); 68853 } 68854 68855 // Add to the end 68856 tracker.push(sRecordId); 68857 this._aSelections = tracker; 68858 68859 // Update trackers 68860 if(!this._oAnchorRecord) { 68861 this._oAnchorRecord = oRecord; 68862 } 68863 68864 // Update UI 68865 if(elRow) { 68866 Dom.addClass(elRow, DT.CLASS_SELECTED); 68867 } 68868 68869 this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow}); 68870 YAHOO.log("Selected " + elRow, "info", this.toString()); 68871 } 68872 else { 68873 YAHOO.log("Could not select row " + row, "warn", this.toString()); 68874 } 68875}, 68876 68877/** 68878 * Sets given row to the unselected state. 68879 * 68880 * @method unselectRow 68881 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element 68882 * reference or ID string, Record instance, or RecordSet position index. 68883 */ 68884unselectRow : function(row) { 68885 var elRow = this.getTrEl(row); 68886 68887 var oRecord; 68888 if(row instanceof YAHOO.widget.Record) { 68889 oRecord = this._oRecordSet.getRecord(row); 68890 } 68891 else if(lang.isNumber(row)) { 68892 oRecord = this.getRecord(row); 68893 } 68894 else { 68895 oRecord = this.getRecord(elRow); 68896 } 68897 68898 if(oRecord) { 68899 // Update selection trackers 68900 var tracker = this._aSelections || []; 68901 var sRecordId = oRecord.getId(); 68902 var index = -1; 68903 68904 // Use Array.indexOf if available... 68905 if(tracker.indexOf) { 68906 index = tracker.indexOf(sRecordId); 68907 } 68908 // ...or do it the old-fashioned way 68909 else { 68910 for(var j=tracker.length-1; j>-1; j--) { 68911 if(tracker[j] === sRecordId){ 68912 index = j; 68913 break; 68914 } 68915 } 68916 } 68917 if(index > -1) { 68918 // Update tracker 68919 tracker.splice(index,1); 68920 this._aSelections = tracker; 68921 68922 // Update the UI 68923 Dom.removeClass(elRow, DT.CLASS_SELECTED); 68924 68925 this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow}); 68926 YAHOO.log("Unselected " + elRow, "info", this.toString()); 68927 68928 return; 68929 } 68930 } 68931 YAHOO.log("Could not unselect row " + row, "warn", this.toString()); 68932}, 68933 68934/** 68935 * Clears out all row selections. 68936 * 68937 * @method unselectAllRows 68938 */ 68939unselectAllRows : function() { 68940 // Remove all rows from tracker 68941 var tracker = this._aSelections || [], 68942 recId, 68943 removed = []; 68944 for(var j=tracker.length-1; j>-1; j--) { 68945 if(lang.isString(tracker[j])){ 68946 recId = tracker.splice(j,1); 68947 removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId); 68948 } 68949 } 68950 68951 // Update tracker 68952 this._aSelections = tracker; 68953 68954 // Update UI 68955 this._unselectAllTrEls(); 68956 68957 this.fireEvent("unselectAllRowsEvent", {records: removed}); 68958 YAHOO.log("Unselected all rows", "info", this.toString()); 68959}, 68960 68961/** 68962 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED 68963 * from all TD elements in the internal tracker. 68964 * 68965 * @method _unselectAllTdEls 68966 * @private 68967 */ 68968_unselectAllTdEls : function() { 68969 var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody); 68970 Dom.removeClass(selectedCells, DT.CLASS_SELECTED); 68971}, 68972 68973/** 68974 * Returns array of selected TD elements on the page. 68975 * 68976 * @method getSelectedTdEls 68977 * @return {HTMLElement[]} Array of selected TD elements. 68978 */ 68979getSelectedTdEls : function() { 68980 return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody); 68981}, 68982 68983/** 68984 * Sets given cell to the selected state. 68985 * 68986 * @method selectCell 68987 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or 68988 * object literal of syntax {record:oRecord, column:oColumn}. 68989 */ 68990selectCell : function(cell) { 68991//TODO: accept {record} in selectCell() 68992 var elCell = this.getTdEl(cell); 68993 68994 if(elCell) { 68995 var oRecord = this.getRecord(elCell); 68996 var oColumn = this.getColumn(this.getCellIndex(elCell)); 68997 var sColumnKey = oColumn.getKey(); 68998 68999 if(oRecord && sColumnKey) { 69000 // Get Record ID 69001 var tracker = this._aSelections || []; 69002 var sRecordId = oRecord.getId(); 69003 69004 // Remove if there 69005 for(var j=tracker.length-1; j>-1; j--) { 69006 if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){ 69007 tracker.splice(j,1); 69008 break; 69009 } 69010 } 69011 69012 // Add to the end 69013 tracker.push({recordId:sRecordId, columnKey:sColumnKey}); 69014 69015 // Update trackers 69016 this._aSelections = tracker; 69017 if(!this._oAnchorCell) { 69018 this._oAnchorCell = {record:oRecord, column:oColumn}; 69019 } 69020 69021 // Update the UI 69022 Dom.addClass(elCell, DT.CLASS_SELECTED); 69023 69024 this.fireEvent("cellSelectEvent", {record:oRecord, column:oColumn, key: sColumnKey, el:elCell}); 69025 YAHOO.log("Selected " + elCell, "info", this.toString()); 69026 return; 69027 } 69028 } 69029 YAHOO.log("Could not select cell " + cell, "warn", this.toString()); 69030}, 69031 69032/** 69033 * Sets given cell to the unselected state. 69034 * 69035 * @method unselectCell 69036 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or 69037 * object literal of syntax {record:oRecord, column:oColumn}. 69038 * @param cell {HTMLElement | String} DOM element reference or ID string 69039 * to DataTable page element or RecordSet index. 69040 */ 69041unselectCell : function(cell) { 69042 var elCell = this.getTdEl(cell); 69043 69044 if(elCell) { 69045 var oRecord = this.getRecord(elCell); 69046 var oColumn = this.getColumn(this.getCellIndex(elCell)); 69047 var sColumnKey = oColumn.getKey(); 69048 69049 if(oRecord && sColumnKey) { 69050 // Get Record ID 69051 var tracker = this._aSelections || []; 69052 var id = oRecord.getId(); 69053 69054 // Is it selected? 69055 for(var j=tracker.length-1; j>-1; j--) { 69056 if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){ 69057 // Remove from tracker 69058 tracker.splice(j,1); 69059 69060 // Update tracker 69061 this._aSelections = tracker; 69062 69063 // Update the UI 69064 Dom.removeClass(elCell, DT.CLASS_SELECTED); 69065 69066 this.fireEvent("cellUnselectEvent", {record:oRecord, column: oColumn, key:sColumnKey, el:elCell}); 69067 YAHOO.log("Unselected " + elCell, "info", this.toString()); 69068 return; 69069 } 69070 } 69071 } 69072 } 69073 YAHOO.log("Could not unselect cell " + cell, "warn", this.toString()); 69074}, 69075 69076/** 69077 * Clears out all cell selections. 69078 * 69079 * @method unselectAllCells 69080 */ 69081unselectAllCells : function() { 69082 // Remove all cells from tracker 69083 var tracker = this._aSelections || []; 69084 for(var j=tracker.length-1; j>-1; j--) { 69085 if(lang.isObject(tracker[j])){ 69086 tracker.splice(j,1); 69087 } 69088 } 69089 69090 // Update tracker 69091 this._aSelections = tracker; 69092 69093 // Update UI 69094 this._unselectAllTdEls(); 69095 69096 //TODO: send data to unselectAllCellsEvent handler 69097 this.fireEvent("unselectAllCellsEvent"); 69098 YAHOO.log("Unselected all cells", "info", this.toString()); 69099}, 69100 69101/** 69102 * Returns true if given item is selected, false otherwise. 69103 * 69104 * @method isSelected 69105 * @param o {String | HTMLElement | YAHOO.widget.Record | Number 69106 * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by 69107 * reference or ID string, a Record instance, a RecordSet position index, 69108 * or an object literal representation 69109 * of a cell. 69110 * @return {Boolean} True if item is selected. 69111 */ 69112isSelected : function(o) { 69113 if(o && (o.ownerDocument == document)) { 69114 return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED)); 69115 } 69116 else { 69117 var oRecord, sRecordId, j; 69118 var tracker = this._aSelections; 69119 if(tracker && tracker.length > 0) { 69120 // Looking for a Record? 69121 if(o instanceof YAHOO.widget.Record) { 69122 oRecord = o; 69123 } 69124 else if(lang.isNumber(o)) { 69125 oRecord = this.getRecord(o); 69126 } 69127 if(oRecord) { 69128 sRecordId = oRecord.getId(); 69129 69130 // Is it there? 69131 // Use Array.indexOf if available... 69132 if(tracker.indexOf) { 69133 if(tracker.indexOf(sRecordId) > -1) { 69134 return true; 69135 } 69136 } 69137 // ...or do it the old-fashioned way 69138 else { 69139 for(j=tracker.length-1; j>-1; j--) { 69140 if(tracker[j] === sRecordId){ 69141 return true; 69142 } 69143 } 69144 } 69145 } 69146 // Looking for a cel
69146l 69147 else if(o.record && o.column){ 69148 sRecordId = o.record.getId(); 69149 var sColumnKey = o.column.getKey(); 69150 69151 for(j=tracker.length-1; j>-1; j--) { 69152 if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){ 69153 return true; 69154 } 69155 } 69156 } 69157 } 69158 } 69159 return false; 69160}, 69161 69162/** 69163 * Returns selected rows as an array of Record IDs. 69164 * 69165 * @method getSelectedRows 69166 * @return {String[]} Array of selected rows by Record ID. 69167 */ 69168getSelectedRows : function() { 69169 var aSelectedRows = []; 69170 var tracker = this._aSelections || []; 69171 for(var j=0; j<tracker.length; j++) { 69172 if(lang.isString(tracker[j])){ 69173 aSelectedRows.push(tracker[j]); 69174 } 69175 } 69176 return aSelectedRows; 69177}, 69178 69179/** 69180 * Returns selected cells as an array of object literals: 69181 * {recordId:sRecordId, columnKey:sColumnKey}. 69182 * 69183 * @method getSelectedCells 69184 * @return {Object[]} Array of selected cells by Record ID and Column ID. 69185 */ 69186getSelectedCells : function() { 69187 var aSelectedCells = []; 69188 var tracker = this._aSelections || []; 69189 for(var j=0; j<tracker.length; j++) { 69190 if(tracker[j] && lang.isObject(tracker[j])){ 69191 aSelectedCells.push(tracker[j]); 69192 } 69193 } 69194 return aSelectedCells; 69195}, 69196 69197/** 69198 * Returns last selected Record ID. 69199 * 69200 * @method getLastSelectedRecord 69201 * @return {String} Record ID of last selected row. 69202 */ 69203getLastSelectedRecord : function() { 69204 var tracker = this._aSelections; 69205 if(tracker && tracker.length > 0) { 69206 for(var i=tracker.length-1; i>-1; i--) { 69207 if(lang.isString(tracker[i])){ 69208 return tracker[i]; 69209 } 69210 } 69211 } 69212}, 69213 69214/** 69215 * Returns last selected cell as an object literal: 69216 * {recordId:sRecordId, columnKey:sColumnKey}. 69217 * 69218 * @method getLastSelectedCell 69219 * @return {Object} Object literal representation of a cell. 69220 */ 69221getLastSelectedCell : function() { 69222 var tracker = this._aSelections; 69223 if(tracker && tracker.length > 0) { 69224 for(var i=tracker.length-1; i>-1; i--) { 69225 if(tracker[i].recordId && tracker[i].columnKey){ 69226 return tracker[i]; 69227 } 69228 } 69229 } 69230}, 69231 69232/** 69233 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row. 69234 * 69235 * @method highlightRow 69236 * @param row {HTMLElement | String} DOM element reference or ID string. 69237 */ 69238highlightRow : function(row) { 69239 var elRow = this.getTrEl(row); 69240 69241 if(elRow) { 69242 // Make sure previous row is unhighlighted 69243/* if(this._sLastHighlightedTrElId) { 69244 Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED); 69245 }*/ 69246 var oRecord = this.getRecord(elRow); 69247 Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED); 69248 //this._sLastHighlightedTrElId = elRow.id; 69249 this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow}); 69250 YAHOO.log("Highlighted " + elRow, "info", this.toString()); 69251 return; 69252 } 69253 YAHOO.log("Could not highlight row " + row, "warn", this.toString()); 69254}, 69255 69256/** 69257 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row. 69258 * 69259 * @method unhighlightRow 69260 * @param row {HTMLElement | String} DOM element reference or ID string. 69261 */ 69262unhighlightRow : function(row) { 69263 var elRow = this.getTrEl(row); 69264 69265 if(elRow) { 69266 var oRecord = this.getRecord(elRow); 69267 Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED); 69268 this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow}); 69269 YAHOO.log("Unhighlighted " + elRow, "info", this.toString()); 69270 return; 69271 } 69272 YAHOO.log("Could not unhighlight row " + row, "warn", this.toString()); 69273}, 69274 69275/** 69276 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell. 69277 * 69278 * @method highlightCell 69279 * @param cell {HTMLElement | String} DOM element reference or ID string. 69280 */ 69281highlightCell : function(cell) { 69282 var elCell = this.getTdEl(cell); 69283 69284 if(elCell) { 69285 // Make sure previous cell is unhighlighted 69286 if(this._elLastHighlightedTd) { 69287 this.unhighlightCell(this._elLastHighlightedTd); 69288 } 69289 69290 var oRecord = this.getRecord(elCell); 69291 var oColumn = this.getColumn(this.getCellIndex(elCell)); 69292 var sColumnKey = oColumn.getKey(); 69293 Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED); 69294 this._elLastHighlightedTd = elCell; 69295 this.fireEvent("cellHighlightEvent", {record:oRecord, column:oColumn, key:sColumnKey, el:elCell}); 69296 YAHOO.log("Highlighted " + elCell, "info", this.toString()); 69297 return; 69298 } 69299 YAHOO.log("Could not highlight cell " + cell, "warn", this.toString()); 69300}, 69301 69302/** 69303 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell. 69304 * 69305 * @method unhighlightCell 69306 * @param cell {HTMLElement | String} DOM element reference or ID string. 69307 */ 69308unhighlightCell : function(cell) { 69309 var elCell = this.getTdEl(cell); 69310 69311 if(elCell) { 69312 var oRecord = this.getRecord(elCell); 69313 Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED); 69314 this._elLastHighlightedTd = null; 69315 this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(this.getCellIndex(elCell)), key:this.getColumn(this.getCellIndex(elCell)).getKey(), el:elCell}); 69316 YAHOO.log("Unhighlighted " + elCell, "info", this.toString()); 69317 return; 69318 } 69319 YAHOO.log("Could not unhighlight cell " + cell, "warn", this.toString()); 69320}, 69321 69322 69323 69324 69325 69326 69327 69328 69329 69330 69331 69332 69333 69334 69335 69336 69337 69338 69339 69340 69341 69342 69343 69344 69345 69346 69347 69348 69349 69350 69351 69352 69353 69354 69355 69356 69357 69358 69359 69360 69361 69362 69363 69364 69365 69366// INLINE EDITING 69367 69368/** 69369 * Assigns CellEditor instance to existing Column. 69370 * @method addCellEditor 69371 * @param oColumn {YAHOO.widget.Column} Column instance. 69372 * @param oEditor {YAHOO.wdiget.CellEditor} CellEditor instance. 69373 */ 69374addCellEditor : function(oColumn, oEditor) { 69375 oColumn.editor = oEditor; 69376 oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true); 69377 oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true); 69378 oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true); 69379 oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true); 69380 oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true); 69381 oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true); 69382 oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true); 69383 oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true); 69384}, 69385 69386/** 69387 * Returns current CellEditor instance, or null. 69388 * @method getCellEditor 69389 * @return {YAHOO.widget.CellEditor} CellEditor instance. 69390 */ 69391getCellEditor : function() { 69392 return this._oCellEditor; 69393}, 69394 69395 69396/** 69397 * Activates and shows CellEditor instance for the given cell while deactivating and 69398 * canceling previous CellEditor. It is baked into DataTable that only one CellEditor 69399 * can be active at any given time. 69400 * 69401 * @method showCellEditor 69402 * @param elCell {HTMLElement | String} Cell to edit. 69403 */ 69404showCellEditor : function(elCell, oRecord, oColumn) { 69405 // Get a particular CellEditor 69406 elCell = this.getTdEl(elCell); 69407 if(elCell) { 69408 oColumn = this.getColumn(elCell); 69409 if(oColumn && oColumn.editor) { 69410 var oCellEditor = this._oCellEditor;
69411 // Clean up active CellEditor 69412 if(oCellEditor) { 69413 if(this._oCellEditor.cancel) { 69414 this._oCellEditor.cancel(); 69415 } 69416 else if(oCellEditor.isActive) { 69417 this.cancelCellEditor(); 69418 } 69419 } 69420 69421 if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) { 69422 // Get CellEditor 69423 oCellEditor = oColumn.editor; 69424 var ok = oCellEditor.attach(this, elCell); 69425 if(ok) { 69426 oCellEditor.render(); 69427 oCellEditor.move(); 69428 ok = this.doBeforeShowCellEditor(oCellEditor); 69429 if(ok) { 69430 oCellEditor.show(); 69431 this._oCellEditor = oCellEditor; 69432 } 69433 } 69434 } 69435 // Backward compatibility 69436 else { 69437 if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) { 69438 oRecord = this.getRecord(elCell); 69439 } 69440 if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) { 69441 oColumn = this.getColumn(elCell); 69442 } 69443 if(oRecord && oColumn) { 69444 if(!this._oCellEditor || this._oCellEditor.container) { 69445 this._initCellEditorEl(); 69446 } 69447 69448 // Update Editor values 69449 oCellEditor = this._oCellEditor; 69450 oCellEditor.cell = elCell; 69451 oCellEditor.record = oRecord; 69452 oCellEditor.column = oColumn; 69453 oCellEditor.validator = (oColumn.editorOptions && 69454 lang.isFunction(oColumn.editorOptions.validator)) ? 69455 oColumn.editorOptions.validator : null; 69456 oCellEditor.value = oRecord.getData(oColumn.key); 69457 oCellEditor.defaultValue = null; 69458 69459 // Move Editor 69460 var elContainer = oCellEditor.container; 69461 var x = Dom.getX(elCell); 69462 var y = Dom.getY(elCell); 69463 69464 // SF doesn't get xy for cells in scrolling table 69465 // when tbody display is set to block 69466 if(isNaN(x) || isNaN(y)) { 69467 x = elCell.offsetLeft + // cell pos relative to table 69468 Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document 69469 this._elTbody.scrollLeft; // minus tbody scroll 69470 y = elCell.offsetTop + // cell pos relative to table 69471 Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document 69472 this._elTbody.scrollTop + // minus tbody scroll 69473 this._elThead.offsetHeight; // account for fixed THEAD cells 69474 } 69475 69476 elContainer.style.left = x + "px"; 69477 elContainer.style.top = y + "px"; 69478 69479 // Hook to customize the UI 69480 this.doBeforeShowCellEditor(this._oCellEditor); 69481 69482 //TODO: This is temporarily up here due so elements can be focused 69483 // Show Editor 69484 elContainer.style.display = ""; 69485 69486 // Handle ESC key 69487 Ev.addListener(elContainer, "keydown", function(e, oSelf) { 69488 // ESC hides Cell Editor 69489 if((e.keyCode == 27)) { 69490 oSelf.cancelCellEditor(); 69491 oSelf.focusTbodyEl(); 69492 } 69493 else { 69494 oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e}); 69495 } 69496 }, this); 69497 69498 // Render Editor markup 69499 var fnEditor; 69500 if(lang.isString(oColumn.editor)) { 69501 switch(oColumn.editor) { 69502 case "checkbox": 69503 fnEditor = DT.editCheckbox; 69504 break; 69505 case "date": 69506 fnEditor = DT.editDate; 69507 break; 69508 case "dropdown": 69509 fnEditor = DT.editDropdown; 69510 break; 69511 case "radio": 69512 fnEditor = DT.editRadio; 69513 break; 69514 case "textarea": 69515 fnEditor = DT.editTextarea; 69516 break; 69517 case "textbox": 69518 fnEditor = DT.editTextbox; 69519 break; 69520 default: 69521 fnEditor = null; 69522 } 69523 } 69524 else if(lang.isFunction(oColumn.editor)) { 69525 fnEditor = oColumn.editor; 69526 } 69527 69528 if(fnEditor) { 69529 // Create DOM input elements 69530 fnEditor(this._oCellEditor, this); 69531 69532 // Show Save/Cancel buttons 69533 if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) { 69534 this.showCellEditorBtns(elContainer); 69535 } 69536 69537 oCellEditor.isActive = true; 69538 69539 //TODO: verify which args to pass 69540 this.fireEvent("editorShowEvent", {editor:oCellEditor}); 69541 YAHOO.log("Cell Editor shown for " + elCell, "info", this.toString()); 69542 return; 69543 } 69544 } 69545 69546 69547 69548 69549 } 69550 } 69551 } 69552}, 69553 69554/** 69555 * Backward compatibility. 69556 * 69557 * @method _initCellEditorEl 69558 * @private 69559 * @deprecated Use BaseCellEditor class. 69560 */ 69561_initCellEditorEl : function() { 69562 // Attach Cell Editor container element as first child of body 69563 var elCellEditor = document.createElement("div"); 69564 elCellEditor.id = this._sId + "-celleditor"; 69565 elCellEditor.style.display = "none"; 69566 elCellEditor.tabIndex = 0; 69567 Dom.addClass(elCellEditor, DT.CLASS_EDITOR); 69568 var elFirstChild = Dom.getFirstChild(document.body); 69569 if(elFirstChild) { 69570 elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild); 69571 } 69572 else { 69573 elCellEditor = document.body.appendChild(elCellEditor); 69574 } 69575 69576 // Internal tracker of Cell Editor values 69577 var oCellEditor = {}; 69578 oCellEditor.container = elCellEditor; 69579 oCellEditor.value = null; 69580 oCellEditor.isActive = false;
69581 this._oCellEditor = oCellEditor; 69582}, 69583 69584/** 69585 * Overridable abstract method to customize CellEditor before showing. 69586 * 69587 * @method doBeforeShowCellEditor 69588 * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance. 69589 * @return {Boolean} Return true to continue showing CellEditor. 69590 */ 69591doBeforeShowCellEditor : function(oCellEditor) { 69592 return true; 69593}, 69594 69595/** 69596 * Saves active CellEditor input to Record and upates DOM UI. 69597 * 69598 * @method saveCellEditor 69599 */ 69600saveCellEditor : function() { 69601 if(this._oCellEditor) { 69602 if(this._oCellEditor.save) { 69603 this._oCellEditor.save(); 69604 } 69605 // Backward compatibility 69606 else if(this._oCellEditor.isActive) { 69607 var newData = this._oCellEditor.value; 69608 // Copy the data to pass to the event 69609 //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key)); 69610 var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key); 69611 69612 // Validate input data 69613 if(this._oCellEditor.validator) { 69614 newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor); 69615 if(newData === null ) { 69616 this.resetCellEditor(); 69617 this.fireEvent("editorRevertEvent", 69618 {editor:this._oCellEditor, oldData:oldData, newData:newData}); 69619 YAHOO.log("Could not save Cell Editor input due to invalid data " + 69620 lang.dump(newData), "warn", this.toString()); 69621 return; 69622 } 69623 } 69624 // Update the Record 69625 this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value); 69626 // Update the UI 69627 this.formatCell(this._oCellEditor.cell.firstChild, this._oCellEditor.record, this._oCellEditor.column); 69628 69629 // Bug fix 1764044 69630 this._oChainRender.add({ 69631 method: function() { 69632 this.validateColumnWidths(); 69633 }, 69634 scope: this 69635 }); 69636 this._oChainRender.run(); 69637 // Clear out the Cell Editor 69638 this.resetCellEditor(); 69639 69640 this.fireEvent("editorSaveEvent", 69641 {editor:this._oCellEditor, oldData:oldData, newData:newData}); 69642 YAHOO.log("Cell Editor input saved", "info", this.toString()); 69643 } 69644 } 69645}, 69646 69647/** 69648 * Cancels active CellEditor. 69649 * 69650 * @method cancelCellEditor 69651 */ 69652cancelCellEditor : function() { 69653 if(this._oCellEditor) { 69654 if(this._oCellEditor.cancel) { 69655 this._oCellEditor.cancel(); 69656 } 69657 // Backward compatibility 69658 else if(this._oCellEditor.isActive) { 69659 this.resetCellEditor(); 69660 //TODO: preserve values for the event? 69661 this.fireEvent("editorCancelEvent", {editor:this._oCellEditor}); 69662 YAHOO.log("Cell Editor input canceled", "info", this.toString()); 69663 } 69664 69665 YAHOO.log("CellEditor input canceled", "info", this.toString()); 69666 } 69667}, 69668 69669/** 69670 * Destroys active CellEditor instance and UI. 69671 * 69672 * @method destroyCellEditor 69673 */ 69674destroyCellEditor : function() { 69675 if(this._oCellEditor) { 69676 this._oCellEditor.destroy(); 69677 this._oCellEditor = null; 69678 } 69679}, 69680 69681/** 69682 * Passes through showEvent of the active CellEditor. 69683 * 69684 * @method _onEditorShowEvent 69685 * @param oArgs {Object} Custom Event args. 69686 * @private 69687 */ 69688_onEditorShowEvent : function(oArgs) { 69689 this.fireEvent("editorShowEvent", oArgs); 69690}, 69691 69692/** 69693 * Passes through keydownEvent of the active CellEditor. 69694 * @param oArgs {Object} Custom Event args. 69695 * 69696 * @method _onEditorKeydownEvent 69697 * @private 69698 */ 69699_onEditorKeydownEvent : function(oArgs) { 69700 this.fireEvent("editorKeydownEvent", oArgs); 69701}, 69702 69703/** 69704 * Passes through revertEvent of the active CellEditor. 69705 * 69706 * @method _onEditorRevertEvent 69707 * @param oArgs {Object} Custom Event args. 69708 * @private 69709 */ 69710_onEditorRevertEvent : function(oArgs) { 69711 this.fireEvent("editorRevertEvent", oArgs); 69712}, 69713 69714/** 69715 * Passes through saveEvent of the active CellEditor. 69716 * 69717 * @method _onEditorSaveEvent 69718 * @param oArgs {Object} Custom Event args. 69719 * @private 69720 */ 69721_onEditorSaveEvent : function(oArgs) { 69722 this.fireEvent("editorSaveEvent", oArgs); 69723}, 69724 69725/** 69726 * Passes through cancelEvent of the active CellEditor. 69727 * 69728 * @method _onEditorCancelEvent 69729 * @param oArgs {Object} Custom Event args. 69730 * @private 69731 */ 69732_onEditorCancelEvent : function(oArgs) { 69733 this.fireEvent("editorCancelEvent", oArgs); 69734}, 69735 69736/** 69737 * Passes through blurEvent of the active CellEditor. 69738 * 69739 * @method _onEditorBlurEvent 69740 * @param oArgs {Object} Custom Event args. 69741 * @private 69742 */ 69743_onEditorBlurEvent : function(oArgs) { 69744 this.fireEvent("editorBlurEvent", oArgs); 69745}, 69746 69747/** 69748 * Passes through blockEvent of the active CellEditor. 69749 * 69750 * @method _onEditorBlockEvent 69751 * @param oArgs {Object} Custom Event args. 69752 * @private 69753 */ 69754_onEditorBlockEvent : function(oArgs) { 69755 this.fireEvent("editorBlockEvent", oArgs); 69756}, 69757 69758/** 69759 * Passes through unblockEvent of the active CellEditor. 69760 * 69761 * @method _onEditorUnblockEvent 69762 * @param oArgs {Object} Custom Event args. 69763 * @private 69764 */ 69765_onEditorUnblockEvent : function(oArgs) { 69766 this.fireEvent("editorUnblockEvent", oArgs); 69767}, 69768 69769/** 69770 * Public handler of the editorBlurEvent. By default, saves on blur if 69771 * disableBtns is true, otherwise cancels on blur. 69772 * 69773 * @method onEditorBlurEvent 69774 * @param oArgs {Object} Custom Event args. 69775 */ 69776onEditorBlurEvent : function(oArgs) { 69777 if(oArgs.editor.disableBtns) { 69778 // Save on blur 69779 if(oArgs.editor.save) { // Backward incompatible 69780 oArgs.editor.save(); 69781 } 69782 } 69783 else if(oArgs.editor.cancel) { // Backward incompatible 69784 // Cancel on blur 69785 oArgs.editor.cancel(); 69786 } 69787}, 69788 69789/** 69790 * Public handler of the editorBlockEvent. By default, disables DataTable UI. 69791 * 69792 * @method onEditorBlockEvent 69793 * @param oArgs {Object} Custom Event args. 69794 */ 69795onEditorBlockEvent : function(oArgs) { 69796 this.disable(); 69797}, 69798 69799/** 69800 * Public handler of the editorUnblockEvent. By default, undisables DataTable UI. 69801 * 69802 * @method onEditorUnblockEvent 69803 * @param oArgs {Object} Custom Event args. 69804 */ 69805onEditorUnblockEvent : function(oArgs) { 69806 this.undisable(); 69807}, 69808 69809 69810 69811 69812 69813 69814 69815 69816 69817 69818 69819 69820 69821 69822 69823 69824 69825 69826 69827 69828 69829 69830 69831 69832 69833 69834 69835 69836 69837 69838 69839 69840 69841 69842 69843 69844 69845 69846// ABSTRACT METHODS 69847 69848/** 69849 * Overridable method gives implementers a hook to access data before 69850 * it gets added to RecordSet and rendered to the TBODY. 69851 * 69852 * @method doBeforeLoadData 69853 * @param sRequest {String} Original request. 69854 * @param oResponse {Object}
69854 <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 69855 * @param oPayload {MIXED} additional arguments 69856 * @return {Boolean} Return true to continue loading data into RecordSet and 69857 * updating DataTable with new Records, false to cancel. 69858 */ 69859doBeforeLoadData : function(sRequest, oResponse, oPayload) { 69860 return true; 69861}, 69862 69863 69864 69865 69866 69867 69868 69869 69870 69871 69872 69873 69874 69875 69876 69877 69878 69879 69880 69881 69882 69883 69884 69885 69886 69887 69888 69889 69890 69891 69892 69893 69894 69895 69896 69897 69898 69899 69900 69901 69902 69903 69904 69905 69906 69907 69908 69909 69910 69911 69912 69913 69914 69915 69916 69917 69918 69919 69920 69921 69922 69923 69924 69925///////////////////////////////////////////////////////////////////////////// 69926// 69927// Public Custom Event Handlers 69928// 69929///////////////////////////////////////////////////////////////////////////// 69930 69931/** 69932 * Custom event handler to sort Column. 69933 * 69934 * @method onEventSortColumn 69935 * @param oArgs.event {HTMLEvent} Event object. 69936 * @param oArgs.target {HTMLElement} Target element. 69937 */ 69938onEventSortColumn : function(oArgs) { 69939//TODO: support form elements in sortable columns 69940 var evt = oArgs.event; 69941 var target = oArgs.target; 69942 69943 var el = this.getThEl(target) || this.getTdEl(target); 69944 if(el) { 69945 var oColumn = this.getColumn(el); 69946 if(oColumn.sortable) { 69947 Ev.stopEvent(evt); 69948 this.sortColumn(oColumn); 69949 } 69950 } 69951 else { 69952 YAHOO.log("Could not find Column for " + target, "warn", this.toString()); 69953 } 69954}, 69955 69956/** 69957 * Custom event handler to select Column. 69958 * 69959 * @method onEventSelectColumn 69960 * @param oArgs.event {HTMLEvent} Event object. 69961 * @param oArgs.target {HTMLElement} Target element. 69962 */ 69963onEventSelectColumn : function(oArgs) { 69964 this.selectColumn(oArgs.target); 69965}, 69966 69967/** 69968 * Custom event handler to highlight Column. Accounts for spurious 69969 * caused-by-child events. 69970 * 69971 * @method onEventHighlightColumn 69972 * @param oArgs.event {HTMLEvent} Event object. 69973 * @param oArgs.target {HTMLElement} Target element. 69974 */ 69975onEventHighlightColumn : function(oArgs) { 69976 this.highlightColumn(oArgs.target); 69977}, 69978 69979/** 69980 * Custom event handler to unhighlight Column. Accounts for spurious 69981 * caused-by-child events. 69982 * 69983 * @method onEventUnhighlightColumn 69984 * @param oArgs.event {HTMLEvent} Event object. 69985 * @param oArgs.target {HTMLElement} Target element. 69986 */ 69987onEventUnhighlightColumn : function(oArgs) { 69988 this.unhighlightColumn(oArgs.target); 69989}, 69990 69991/** 69992 * Custom event handler to manage selection according to desktop paradigm. 69993 * 69994 * @method onEventSelectRow 69995 * @param oArgs.event {HTMLEvent} Event object. 69996 * @param oArgs.target {HTMLElement} Target element. 69997 */ 69998onEventSelectRow : function(oArgs) { 69999 var sMode = this.get("selectionMode"); 70000 if(sMode == "single") { 70001 this._handleSingleSelectionByMouse(oArgs); 70002 } 70003 else { 70004 this._handleStandardSelectionByMouse(oArgs); 70005 } 70006}, 70007 70008/** 70009 * Custom event handler to select cell. 70010 * 70011 * @method onEventSelectCell 70012 * @param oArgs.event {HTMLEvent} Event object. 70013 * @param oArgs.target {HTMLElement} Target element. 70014 */ 70015onEventSelectCell : function(oArgs) { 70016 var sMode = this.get("selectionMode"); 70017 if(sMode == "cellblock") { 70018 this._handleCellBlockSelectionByMouse(oArgs); 70019 } 70020 else if(sMode == "cellrange") { 70021 this._handleCellRangeSelectionByMouse(oArgs); 70022 } 70023 else { 70024 this._handleSingleCellSelectionByMouse(oArgs); 70025 } 70026}, 70027 70028/** 70029 * Custom event handler to highlight row. Accounts for spurious 70030 * caused-by-child events. 70031 * 70032 * @method onEventHighlightRow 70033 * @param oArgs.event {HTMLEvent} Event object. 70034 * @param oArgs.target {HTMLElement} Target element. 70035 */ 70036onEventHighlightRow : function(oArgs) { 70037 this.highlightRow(oArgs.target); 70038}, 70039 70040/** 70041 * Custom event handler to unhighlight row. Accounts for spurious 70042 * caused-by-child events. 70043 * 70044 * @method onEventUnhighlightRow 70045 * @param oArgs.event {HTMLEvent} Event object. 70046 * @param oArgs.target {HTMLElement} Target element. 70047 */ 70048onEventUnhighlightRow : function(oArgs) { 70049 this.unhighlightRow(oArgs.target); 70050}, 70051 70052/** 70053 * Custom event handler to highlight cell. Accounts for spurious 70054 * caused-by-child events. 70055 * 70056 * @method onEventHighlightCell 70057 * @param oArgs.event {HTMLEvent} Event object. 70058 * @param oArgs.target {HTMLElement} Target element. 70059 */ 70060onEventHighlightCell : function(oArgs) { 70061 this.highlightCell(oArgs.target); 70062}, 70063 70064/** 70065 * Custom event handler to unhighlight cell. Accounts for spurious 70066 * caused-by-child events. 70067 * 70068 * @method onEventUnhighlightCell 70069 * @param oArgs.event {HTMLEvent} Event object. 70070 * @param oArgs.target {HTMLElement} Target element. 70071 */ 70072onEventUnhighlightCell : function(oArgs) { 70073 this.unhighlightCell(oArgs.target); 70074}, 70075 70076/** 70077 * Custom event handler to format cell. 70078 * 70079 * @method onEventFormatCell 70080 * @param oArgs.event {HTMLEvent} Event object. 70081 * @param oArgs.target {HTMLElement} Target element. 70082 */ 70083onEventFormatCell : function(oArgs) { 70084 var target = oArgs.target; 70085 70086 var elCell = this.getTdEl(target); 70087 if(elCell) { 70088 var oColumn = this.getColumn(this.getCellIndex(elCell)); 70089 this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn); 70090 } 70091 else { 70092 YAHOO.log("Could not format cell " + target, "warn", this.toString()); 70093 } 70094}, 70095 70096/** 70097 * Custom event handler to edit cell. 70098 * 70099 * @method onEventShowCellEditor 70100 * @param oArgs.event {HTMLEvent} Event object. 70101 * @param oArgs.target {HTMLElement} Target element. 70102 */ 70103onEventShowCellEditor : function(oArgs) { 70104 if(!this.isDisabled()) { 70105 this.showCellEditor(oArgs.target); 70106 } 70107}, 70108 70109/** 70110 * Custom event handler to save active CellEditor input. 70111 * 70112 * @method onEventSaveCellEditor 70113 */ 70114onEventSaveCellEditor : function(oArgs) { 70115 if(this._oCellEditor) { 70116 if(this._oCellEditor.save) { 70117 this._oCellEditor.save(); 70118 } 70119 // Backward compatibility 70120 else { 70121 this.saveCellEditor(); 70122 } 70123 } 70124}, 70125 70126/** 70127 * Custom event handler to cancel active CellEditor. 70128 * 70129 * @method onEventCancelCellEditor 70130 */ 70131onEventCancelCellEditor : function(oArgs) { 70132 if(this._oCellEditor) { 70133 if(this._oCellEditor.cancel) { 70134 this._oCellEditor.cancel(); 70135 } 70136 // Backward compatibility 70137 else { 70138 this.cancelCellEditor(); 70139 } 70140 } 70141}, 70142 70143/** 70144 * Callback function receives data from DataSource and populates an entire 70145 * DataTable with Records and TR elements, clearing previous Records, if any. 70146 * 70147 * @method onDataReturnInitializeTable 70148 * @param sRequest {String} Original request. 70149 * @param oResponse {Object}
70149 <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70150 * @param oPayload {MIXED} (optional) Additional argument(s) 70151 */ 70152onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) { 70153 if((this instanceof DT) && this._sId) { 70154 this.initializeTable(); 70155 70156 this.onDataReturnSetRows(sRequest,oResponse,oPayload); 70157 } 70158}, 70159 70160/** 70161 * Callback function receives reponse from DataSource, replaces all existing 70162 * Records in RecordSet, updates TR elements with new data, and updates state 70163 * UI for pagination and sorting from payload data, if necessary. 70164 * 70165 * @method onDataReturnReplaceRows 70166 * @param oRequest {MIXED} Original generated request. 70167 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70168 * @param oPayload {MIXED} (optional) Additional argument(s) 70169 */ 70170onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) { 70171 if((this instanceof DT) && this._sId) { 70172 this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload}); 70173 70174 // Pass data through abstract method for any transformations 70175 var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload), 70176 pag = this.get('paginator'), 70177 index = 0; 70178 70179 // Data ok to set 70180 if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) { 70181 // Update Records 70182 this._oRecordSet.reset(); 70183 70184 if (this.get('dynamicData')) { 70185 if (oPayload && oPayload.pagination && 70186 lang.isNumber(oPayload.pagination.recordOffset)) { 70187 index = oPayload.pagination.recordOffset; 70188 } else if (pag) { 70189 index = pag.getStartIndex(); 70190 } 70191 } 70192 70193 this._oRecordSet.setRecords(oResponse.results, index | 0); 70194 70195 // Update state 70196 this._handleDataReturnPayload(oRequest, oResponse, oPayload); 70197 70198 // Update UI 70199 this.render(); 70200 } 70201 // Error 70202 else if(ok && oResponse.error) { 70203 this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR); 70204 } 70205 } 70206}, 70207 70208/** 70209 * Callback function receives data from DataSource and appends to an existing 70210 * DataTable new Records and, if applicable, creates or updates 70211 * corresponding TR elements. 70212 * 70213 * @method onDataReturnAppendRows 70214 * @param sRequest {String} Original request. 70215 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70216 * @param oPayload {MIXED} (optional) Additional argument(s) 70217 */ 70218onDataReturnAppendRows : function(sRequest, oResponse, oPayload) { 70219 if((this instanceof DT) && this._sId) { 70220 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload}); 70221 70222 // Pass data through abstract method for any transformations 70223 var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload); 70224 70225 // Data ok to append 70226 if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) { 70227 // Append rows 70228 this.addRows(oResponse.results); 70229 70230 // Update state 70231 this._handleDataReturnPayload(sRequest, oResponse, oPayload); 70232 } 70233 // Error 70234 else if(ok && oResponse.error) { 70235 this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR); 70236 } 70237 } 70238}, 70239 70240/** 70241 * Callback function receives data from DataSource and inserts new records 70242 * starting at the index specified in oPayload.insertIndex. The value for 70243 * oPayload.insertIndex can be populated when sending the request to the DataSource, 70244 * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime. 70245 * If applicable, creates or updates corresponding TR elements. 70246 * 70247 * @method onDataReturnInsertRows 70248 * @param sRequest {String} Original request. 70249 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70250 * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex. 70251 */ 70252onDataReturnInsertRows : function(sRequest, oResponse, oPayload) { 70253 if((this instanceof DT) && this._sId) { 70254 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload}); 70255 70256 // Pass data through abstract method for any transformations 70257 var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload); 70258 70259 // Data ok to append 70260 if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) { 70261 // Insert rows 70262 this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0)); 70263 70264 // Update state 70265 this._handleDataReturnPayload(sRequest, oResponse, oPayload); 70266 } 70267 // Error 70268 else if(ok && oResponse.error) { 70269 this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR); 70270 } 70271 } 70272}, 70273 70274/** 70275 * Callback function receives data from DataSource and incrementally updates Records 70276 * starting at the index specified in oPayload.updateIndex. The value for 70277 * oPayload.updateIndex can be populated when sending the request to the DataSource, 70278 * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime. 70279 * If applicable, creates or updates corresponding TR elements. 70280 * 70281 * @method onDataReturnUpdateRows 70282 * @param sRequest {String} Original request. 70283 * @param oResponse {Object}
70283 <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70284 * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex. 70285 */ 70286onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) { 70287 if((this instanceof DT) && this._sId) { 70288 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload}); 70289 70290 // Pass data through abstract method for any transformations 70291 var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload); 70292 70293 // Data ok to append 70294 if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) { 70295 // Insert rows 70296 this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results); 70297 70298 // Update state 70299 this._handleDataReturnPayload(sRequest, oResponse, oPayload); 70300 } 70301 // Error 70302 else if(ok && oResponse.error) { 70303 this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR); 70304 } 70305 } 70306}, 70307 70308/** 70309 * Callback function receives reponse from DataSource and populates the 70310 * RecordSet with the results. 70311 * 70312 * @method onDataReturnSetRows 70313 * @param oRequest {MIXED} Original generated request. 70314 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70315 * @param oPayload {MIXED} (optional) Additional argument(s) 70316 */ 70317onDataReturnSetRows : function(oRequest, oResponse, oPayload) { 70318 if((this instanceof DT) && this._sId) { 70319 this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload}); 70320 70321 // Pass data through abstract method for any transformations 70322 var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload), 70323 pag = this.get('paginator'), 70324 index = 0; 70325 70326 // Data ok to set 70327 if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) { 70328 // Update Records 70329 if (this.get('dynamicData')) { 70330 if (oPayload && oPayload.pagination && 70331 lang.isNumber(oPayload.pagination.recordOffset)) { 70332 index = oPayload.pagination.recordOffset; 70333 } else if (pag) { 70334 index = pag.getStartIndex(); 70335 } 70336 70337 this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default 70338 } 70339 70340 this._oRecordSet.setRecords(oResponse.results, index | 0); 70341 70342 // Update state 70343 this._handleDataReturnPayload(oRequest, oResponse, oPayload); 70344 70345 // Update UI 70346 this.render(); 70347 } 70348 // Error 70349 else if(ok && oResponse.error) { 70350 this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR); 70351 } 70352 } 70353 else { 70354 YAHOO.log("Instance destroyed before data returned.","info",this.toString()); 70355 } 70356}, 70357 70358/** 70359 * Hook to update oPayload before consumption. 70360 * 70361 * @method handleDataReturnPayload 70362 * @param oRequest {MIXED} Original generated request. 70363 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70364 * @param oPayload {MIXED} State values. 70365 * @return oPayload {MIXED} State values. 70366 */ 70367handleDataReturnPayload : function (oRequest, oResponse, oPayload) { 70368 return oPayload || {}; 70369}, 70370 70371/** 70372 * Updates the DataTable with state data sent in an onDataReturn* payload. 70373 * 70374 * @method _handleDataReturnPayload 70375 * @param oRequest {MIXED} Original generated request. 70376 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>. 70377 * @param oPayload {MIXED} State values 70378 * @private 70379 */ 70380_handleDataReturnPayload : function (oRequest, oResponse, oPayload) { 70381 oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload); 70382 if(oPayload) { 70383 // Update pagination 70384 var oPaginator = this.get('paginator'); 70385 if (oPaginator) { 70386 // Update totalRecords 70387 if(this.get("dynamicData")) { 70388 if (widget.Paginator.isNumeric(oPayload.totalRecords)) { 70389 oPaginator.set('totalRecords',oPayload.totalRecords); 70390 } 70391 } 70392 else { 70393 oPaginator.set('totalRecords',this._oRecordSet.getLength()); 70394 } 70395 // Update other paginator values 70396 if (lang.isObject(oPayload.pagination)) { 70397 oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage); 70398 oPaginator.set('recordOffset',oPayload.pagination.recordOffset); 70399 } 70400 } 70401 70402 // Update sorting 70403 if (oPayload.sortedBy) { 70404 // Set the sorting values in preparation for refresh 70405 this.set('sortedBy', oPayload.sortedBy); 70406 } 70407 // Backwards compatibility for sorting 70408 else if (oPayload.sorting) { 70409 // Set the sorting values in preparation for refresh 70410 this.set('sortedBy', oPayload.sorting); 70411 } 70412 } 70413}, 70414 70415 70416 70417 70418 70419 70420 70421 70422 70423 70424 70425 70426 70427 70428 70429 70430 70431 70432 70433 70434 70435 70436 70437 70438 70439 70440 70441 70442 70443 70444 70445 70446
70447 ///////////////////////////////////////////////////////////////////////////// 70448 // 70449 // Custom Events 70450 // 70451 ///////////////////////////////////////////////////////////////////////////// 70452 70453 /** 70454 * Fired when the DataTable's rows are rendered from an initialized state. 70455 * 70456 * @event initEvent 70457 */ 70458 70459 /** 70460 * Fired before the DataTable's DOM is rendered or modified. 70461 * 70462 * @event beforeRenderEvent 70463 */ 70464 70465 /** 70466 * Fired when the DataTable's DOM is rendered or modified. 70467 * 70468 * @event renderEvent 70469 */ 70470 70471 /** 70472 * Fired when the DataTable's post-render routine is complete, including 70473 * Column width validations. 70474 * 70475 * @event postRenderEvent 70476 */ 70477 70478 /** 70479 * Fired when the DataTable is disabled. 70480 * 70481 * @event disableEvent 70482 */ 70483 70484 /** 70485 * Fired when the DataTable is undisabled. 70486 * 70487 * @event undisableEvent 70488 */ 70489 70490 /** 70491 * Fired when data is returned from DataSource but before it is consumed by 70492 * DataTable. 70493 * 70494 * @event dataReturnEvent 70495 * @param oArgs.request {String} Original request. 70496 * @param oArgs.response {Object} Response object. 70497 */ 70498 70499 /** 70500 * Fired when the DataTable has a focus event. 70501 * 70502 * @event tableFocusEvent 70503 */ 70504 70505 /** 70506 * Fired when the DataTable THEAD element has a focus event. 70507 * 70508 * @event theadFocusEvent 70509 */ 70510 70511 /** 70512 * Fired when the DataTable TBODY element has a focus event. 70513 * 70514 * @event tbodyFocusEvent 70515 */ 70516 70517 /** 70518 * Fired when the DataTable has a blur event. 70519 * 70520 * @event tableBlurEvent 70521 */ 70522 70523 /*TODO implement theadBlurEvent 70524 * Fired when the DataTable THEAD element has a blur event. 70525 * 70526 * @event theadBlurEvent 70527 */ 70528 70529 /*TODO: implement tbodyBlurEvent 70530 * Fired when the DataTable TBODY element has a blur event. 70531 * 70532 * @event tbodyBlurEvent 70533 */ 70534 70535 /** 70536 * Fired when the DataTable has a key event. 70537 * 70538 * @event tableKeyEvent 70539 * @param oArgs.event {HTMLEvent} The event object. 70540 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70541 */ 70542 70543 /** 70544 * Fired when the DataTable THEAD element has a key event. 70545 * 70546 * @event theadKeyEvent 70547 * @param oArgs.event {HTMLEvent} The event object. 70548 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70549 */ 70550 70551 /** 70552 * Fired when the DataTable TBODY element has a key event. 70553 * 70554 * @event tbodyKeyEvent 70555 * @param oArgs.event {HTMLEvent} The event object. 70556 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70557 */ 70558 70559 /** 70560 * Fired when the DataTable has a mouseover. 70561 * 70562 * @event tableMouseoverEvent 70563 * @param oArgs.event {HTMLEvent} The event object. 70564 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70565 * 70566 */ 70567 70568 /** 70569 * Fired when the DataTable has a mouseout. 70570 * 70571 * @event tableMouseoutEvent 70572 * @param oArgs.event {HTMLEvent} The event object. 70573 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70574 * 70575 */ 70576 70577 /** 70578 * Fired when the DataTable has a mousedown. 70579 * 70580 * @event tableMousedownEvent 70581 * @param oArgs.event {HTMLEvent} The event object. 70582 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70583 * 70584 */ 70585 70586 /** 70587 * Fired when the DataTable has a mouseup. 70588 * 70589 * @event tableMouseupEvent 70590 * @param oArgs.event {HTMLEvent} The event object. 70591 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70592 * 70593 */ 70594 70595 /** 70596 * Fired when the DataTable has a click. 70597 * 70598 * @event tableClickEvent 70599 * @param oArgs.event {HTMLEvent} The event object. 70600 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70601 * 70602 */ 70603 70604 /** 70605 * Fired when the DataTable has a dblclick. 70606 * 70607 * @event tableDblclickEvent 70608 * @param oArgs.event {HTMLEvent} The event object. 70609 * @param oArgs.target {HTMLElement} The DataTable's TABLE element. 70610 * 70611 */ 70612 70613 /** 70614 * Fired when a message is shown in the DataTable's message element. 70615 * 70616 * @event tableMsgShowEvent 70617 * @param oArgs.html {HTML} The HTML displayed. 70618 * @param oArgs.className {String} The className assigned. 70619 * 70620 */ 70621 70622 /** 70623 * Fired when the DataTable's message element is hidden. 70624 * 70625 * @event tableMsgHideEvent 70626 */ 70627 70628 /** 70629 * Fired when a THEAD row has a mouseover. 70630 * 70631 * @event theadRowMouseoverEvent 70632 * @param oArgs.event {HTMLEvent} The event object. 70633 * @param oArgs.target {HTMLElement} The TR element. 70634 */ 70635 70636 /** 70637 * Fired when a THEAD row has a mouseout. 70638 * 70639 * @event theadRowMouseoutEvent 70640 * @param oArgs.event {HTMLEvent} The event object. 70641 * @param oArgs.target {HTMLElement} The TR element. 70642 */ 70643 70644 /** 70645 * Fired when a THEAD row has a mousedown. 70646 * 70647 * @event theadRowMousedownEvent 70648 * @param oArgs.event {HTMLEvent} The event object. 70649 * @param oArgs.target {HTMLElement} The TR element. 70650 */ 70651 70652 /** 70653 * Fired when a THEAD row has a mouseup. 70654 * 70655 * @event theadRowMouseupEvent 70656 * @param oArgs.event {HTMLEvent} The event object. 70657 * @param oArgs.target {HTMLElement} The TR element. 70658 */ 70659 70660 /** 70661 * Fired when a THEAD row has a click. 70662 * 70663 * @event theadRowClickEvent 70664 * @param oArgs.event {HTMLEvent} The event object. 70665 * @param oArgs.target {HTMLElement} The TR element. 70666 */ 70667 70668 /** 70669 * Fired when a THEAD row has a dblclick. 70670 * 70671 * @event theadRowDblclickEvent 70672 * @param oArgs.event {HTMLEvent} The event object. 70673 * @param oArgs.target {HTMLElement} The TR element. 70674 */ 70675 70676 /** 70677 * Fired when a THEAD cell has a mouseover. 70678 * 70679 * @event theadCellMouseoverEvent 70680 * @param oArgs.event {HTMLEvent} The event object. 70681 * @param oArgs.target {HTMLElement} The TH element. 70682 * 70683 */ 70684 70685 /** 70686 * Fired when a THEAD cell has a mouseout. 70687 * 70688 * @event theadCellMouseoutEvent 70689 * @param oArgs.event {HTMLEvent} The event object. 70690 * @param oArgs.target {HTMLElement} The TH element. 70691 * 70692 */ 70693 70694 /** 70695 * Fired when a THEAD cell has a mousedown. 70696 * 70697 * @event theadCellMousedownEvent 70698 * @param oArgs.event {HTMLEvent} The event object. 70699 * @param oArgs.target {HTMLElement} The TH element. 70700 */ 70701 70702 /** 70703 * Fired when a THEAD cell has a mouseup. 70704 * 70705 * @event theadCellMouseupEvent 70706 * @param oArgs.event {HTMLEvent} The event object. 70707 * @param oArgs.target {HTMLElement} The TH element. 70708 */ 70709 70710 /** 70711 * Fired when a THEAD cell has a click. 70712 * 70713 * @event theadCellClickEvent 70714 * @param oArgs.event {HTMLEvent} The event object. 70715 * @param oArgs.target {HTMLElement} The TH element. 70716 */ 70717 70718 /** 70719 * Fired when a THEAD cell has a dblclick. 70720 * 70721 * @event theadCellDblclickEvent 70722 * @param oArgs.event {HTMLEvent} The event object. 70723 * @param oArgs.target {HTMLElement} The TH element. 70724 */ 70725 70726 /** 70727 * Fired when a THEAD label has a mouseover. 70728 * 70729 * @event theadLabelMouseoverEvent 70730 * @param oArgs.event {HTMLEvent} The event object. 70731 * @param oArgs.target {HTMLElement} The SPAN element. 70732 * 70733 */ 70734 70735 /** 70736 * Fired when a THEAD label has a mouseout. 70737 * 70738 * @event theadLabelMouseoutEvent 70739 * @param oArgs.event {HTMLEvent} The event object. 70740 * @param oArgs.target {HTMLElement} The SPAN element. 70741 * 70742 */ 70743 70744 /** 70745 * Fired when a THEAD label has a mousedown. 70746 * 70747 * @event theadLabelMousedownEvent 70748 * @param oArgs.event {HTMLEvent} The event object. 70749 * @param oArgs.target {HTMLElement} The SPAN element. 70750 */ 70751 70752 /** 70753 * Fired when a THEAD label has a mouseup. 70754 * 70755 * @event theadLabelMouseupEvent 70756 * @param oArgs.event {HTMLEvent} The event object. 70757 * @param oArgs.target {HTMLElement} The SPAN element. 70758 */ 70759 70760 /** 70761 * Fired when a THEAD label has a click. 70762 * 70763 * @event theadLabelClickEvent 70764 * @param oArgs.event {HTMLEvent} The event object. 70765 * @param oArgs.target {HTMLElement} The SPAN element. 70766 */ 70767 70768 /** 70769 * Fired when a THEAD label has a dblclick. 70770 * 70771 * @event theadLabelDblclickEvent 70772 * @param oArgs.event {HTMLEvent} The event object. 70773 * @param oArgs.target {HTMLElement} The SPAN element. 70774 */ 70775 70776 /** 70777 * Fired when a column is sorted. 70778 * 70779 * @event columnSortEvent 70780 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70781 * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC 70782 * or YAHOO.widget.DataTable.CLASS_DESC. 70783 */ 70784 70785 /** 70786 * Fired when a column width is set. 70787 * 70788 * @event columnSetWidthEvent 70789 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70790 * @param oArgs.width {Number} The width in pixels. 70791 */ 70792 70793 /** 70794 * Fired when a column width is unset. 70795 * 70796 * @event columnUnsetWidthEvent 70797 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70798 */ 70799 70800 /** 70801 * Fired when a column is drag-resized. 70802 * 70803 * @event columnResizeEvent 70804 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70805 * @param oArgs.target {HTMLElement} The TH element. 70806 * @param oArgs.width {Number} Width in pixels. 70807 */ 70808 70809 /** 70810 * Fired when a Column is moved to a new index. 70811 * 70812 * @event columnReorderEvent 70813 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70814 * @param oArgs.oldIndex {Number} The previous tree index position. 70815 */ 70816 70817 /** 70818 * Fired when a column is hidden. 70819 * 70820 * @event columnHideEvent 70821 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70822 */ 70823 70824 /** 70825 * Fired when a column is shown.
70826 * 70827 * @event columnShowEvent 70828 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70829 */ 70830 70831 /** 70832 * Fired when a column is selected. 70833 * 70834 * @event columnSelectEvent 70835 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70836 */ 70837 70838 /** 70839 * Fired when a column is unselected. 70840 * 70841 * @event columnUnselectEvent 70842 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70843 */ 70844 /** 70845 * Fired when a column is removed. 70846 * 70847 * @event columnRemoveEvent 70848 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70849 */ 70850 70851 /** 70852 * Fired when a column is inserted. 70853 * 70854 * @event columnInsertEvent 70855 * @param oArgs.column {YAHOO.widget.Column} The Column instance. 70856 * @param oArgs.index {Number} The index position. 70857 */ 70858 70859 /** 70860 * Fired when a column is highlighted. 70861 * 70862 * @event columnHighlightEvent 70863 * @param oArgs.column {YAHOO.widget.Column} The highlighted Column. 70864 */ 70865 70866 /** 70867 * Fired when a column is unhighlighted. 70868 * 70869 * @event columnUnhighlightEvent 70870 * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column. 70871 */ 70872 70873 70874 /** 70875 * Fired when a row has a mouseover. 70876 * 70877 * @event rowMouseoverEvent 70878 * @param oArgs.event {HTMLEvent} The event object. 70879 * @param oArgs.target {HTMLElement} The TR element. 70880 */ 70881 70882 /** 70883 * Fired when a row has a mouseout. 70884 * 70885 * @event rowMouseoutEvent 70886 * @param oArgs.event {HTMLEvent} The event object. 70887 * @param oArgs.target {HTMLElement} The TR element. 70888 */ 70889 70890 /** 70891 * Fired when a row has a mousedown. 70892 * 70893 * @event rowMousedownEvent 70894 * @param oArgs.event {HTMLEvent} The event object. 70895 * @param oArgs.target {HTMLElement} The TR element. 70896 */ 70897 70898 /** 70899 * Fired when a row has a mouseup. 70900 * 70901 * @event rowMouseupEvent 70902 * @param oArgs.event {HTMLEvent} The event object. 70903 * @param oArgs.target {HTMLElement} The TR element. 70904 */ 70905 70906 /** 70907 * Fired when a row has a click. 70908 * 70909 * @event rowClickEvent 70910 * @param oArgs.event {HTMLEvent} The event object. 70911 * @param oArgs.target {HTMLElement} The TR element. 70912 */ 70913 70914 /** 70915 * Fired when a row has a dblclick. 70916 * 70917 * @event rowDblclickEvent 70918 * @param oArgs.event {HTMLEvent} The event object. 70919 * @param oArgs.target {HTMLElement} The TR element. 70920 */ 70921 70922 /** 70923 * Fired when a row is added. 70924 * 70925 * @event rowAddEvent 70926 * @param oArgs.record {YAHOO.widget.Record} The added Record. 70927 */ 70928 70929 /** 70930 * Fired when rows are added. 70931 * 70932 * @event rowsAddEvent 70933 * @param oArgs.record {YAHOO.widget.Record[]} The added Records. 70934 */ 70935 70936 /** 70937 * Fired when a row is updated. 70938 * 70939 * @event rowUpdateEvent 70940 * @param oArgs.record {YAHOO.widget.Record} The updated Record. 70941 * @param oArgs.oldData {Object} Object literal of the old data. 70942 */ 70943 70944 /** 70945 * Fired when a row is deleted. 70946 * 70947 * @event rowDeleteEvent 70948 * @param oArgs.oldData {Object} Object literal of the deleted data. 70949 * @param oArgs.recordIndex {Number} Index of the deleted Record. 70950 * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page. 70951 */ 70952 70953 /** 70954 * Fired when rows are deleted. 70955 * 70956 * @event rowsDeleteEvent 70957 * @param oArgs.oldData {Object[]} Array of object literals of the deleted data. 70958 * @param oArgs.recordIndex {Number} Index of the first deleted Record. 70959 * @param oArgs.count {Number} Number of deleted Records. 70960 */ 70961 70962 /** 70963 * Fired when a row is selected. 70964 * 70965 * @event rowSelectEvent 70966 * @param oArgs.el {HTMLElement} The selected TR element, if applicable. 70967 * @param oArgs.record {YAHOO.widget.Record} The selected Record. 70968 */ 70969 70970 /** 70971 * Fired when a row is unselected. 70972 * 70973 * @event rowUnselectEvent 70974 * @param oArgs.el {HTMLElement} The unselected TR element, if applicable. 70975 * @param oArgs.record {YAHOO.widget.Record} The unselected Record. 70976 */ 70977 70978 /** 70979 * Fired when all row selections are cleared. 70980 * 70981 * @event unselectAllRowsEvent 70982 */ 70983 70984 /** 70985 * Fired when a row is highlighted. 70986 * 70987 * @event rowHighlightEvent 70988 * @param oArgs.el {HTMLElement} The highlighted TR element. 70989 * @param oArgs.record {YAHOO.widget.Record} The highlighted Record. 70990 */ 70991 70992 /** 70993 * Fired when a row is unhighlighted. 70994 * 70995 * @event rowUnhighlightEvent 70996 * @param oArgs.el {HTMLElement} The highlighted TR element. 70997 * @param oArgs.record {YAHOO.widget.Record} The highlighted Record. 70998 */ 70999 71000 /** 71001 * Fired when a cell is updated. 71002 * 71003 * @event cellUpdateEvent 71004 * @param oArgs.record {YAHOO.widget.Record} The updated Record. 71005 * @param oArgs.column {YAHOO.widget.Column} The updated Column. 71006 * @param oArgs.oldData {Object} Original data value of the updated cell. 71007 */ 71008 71009 /** 71010 * Fired when a cell has a mouseover. 71011 * 71012 * @event cellMouseoverEvent 71013 * @param oArgs.event {HTMLEvent} The event object. 71014 * @param oArgs.target {HTMLElement} The TD element. 71015 */ 71016 71017 /** 71018 * Fired when a cell has a mouseout. 71019 * 71020 * @event cellMouseoutEvent 71021 * @param oArgs.event {HTMLEvent} The event object. 71022 * @param oArgs.target {HTMLElement} The TD element. 71023 */ 71024 71025 /** 71026 * Fired when a cell has a mousedown. 71027 * 71028 * @event cellMousedownEvent 71029 * @param oArgs.event {HTMLEvent} The event object. 71030 * @param oArgs.target {HTMLElement} The TD element. 71031 */ 71032 71033 /** 71034 * Fired when a cell has a mouseup. 71035 * 71036 * @event cellMouseupEvent 71037 * @param oArgs.event {HTMLEvent} The event object. 71038 * @param oArgs.target {HTMLElement} The TD element. 71039 */ 71040 71041 /** 71042 * Fired when a cell has a click. 71043 * 71044 * @event cellClickEvent 71045 * @param oArgs.event {HTMLEvent} The event object. 71046 * @param oArgs.target {HTMLElement} The TD element. 71047 */ 71048 71049 /** 71050 * Fired when a cell has a dblclick. 71051 * 71052 * @event cellDblclickEvent 71053 * @param oArgs.event {HTMLEvent} The event object. 71054 * @param oArgs.target {HTMLElement} The TD element. 71055 */ 71056 71057 /** 71058 * Fired when a cell is formatted. 71059 * 71060 * @event cellFormatEvent 71061 * @param oArgs.el {HTMLElement} The formatted TD element. 71062 * @param oArgs.record {YAHOO.widget.Record} The associated Record instance. 71063 * @param oArgs.column {YAHOO.widget.Column} The associated Column instance. 71064 * @param oArgs.key {String} (deprecated) The key of the formatted cell. 71065 */ 71066 71067 /** 71068 * Fired when a cell is selected. 71069 * 71070 * @event cellSelectEvent 71071 * @param oArgs.el {HTMLElement} The selected TD element. 71072 * @param oArgs.record {YAHOO.widget.Record} The associated Record instance. 71073 * @param oArgs.column {YAHOO.widget.Column} The associated Column instance. 71074 * @param oArgs.key {String} (deprecated) The key of the selected cell. 71075 */ 71076 71077 /** 71078 * Fired when a cell is unselected. 71079 * 71080 * @event cellUnselectEvent 71081 * @param oArgs.el {HTMLElement} The unselected TD element. 71082 * @param oArgs.record {YAHOO.widget.Record} The associated Record. 71083 * @param oArgs.column {YAHOO.widget.Column} The associated Column instance. 71084 * @param oArgs.key {String} (deprecated) The key of the unselected cell. 71085 71086 */ 71087 71088 /** 71089 * Fired when a cell is highlighted. 71090 * 71091 * @event cellHighlightEvent 71092 * @param oArgs.el {HTMLElement} The highlighted TD element. 71093 * @param oArgs.record {YAHOO.widget.Record} The associated Record instance. 71094 * @param oArgs.column {YAHOO.widget.Column} The associated Column instance. 71095 * @param oArgs.key {String} (deprecated) The key of the highlighted cell. 71096 71097 */ 71098 71099 /** 71100 * Fired when a cell is unhighlighted. 71101 * 71102 * @event cellUnhighlightEvent 71103 * @param oArgs.el {HTMLElement} The unhighlighted TD element. 71104 * @param oArgs.record {YAHOO.widget.Record} The associated Record instance. 71105 * @param oArgs.column {YAHOO.widget.Column} The associated Column instance. 71106 * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell. 71107 71108 */ 71109 71110 /** 71111 * Fired when all cell selections are cleared. 71112 * 71113 * @event unselectAllCellsEvent 71114 */ 71115 71116 /** 71117 * Fired when a CellEditor is shown.
71118 * 71119 * @event editorShowEvent 71120 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71121 */ 71122 71123 /** 71124 * Fired when a CellEditor has a keydown. 71125 * 71126 * @event editorKeydownEvent 71127 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71128 * @param oArgs.event {HTMLEvent} The event object. 71129 */ 71130 71131 /** 71132 * Fired when a CellEditor input is reverted. 71133 * 71134 * @event editorRevertEvent 71135 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71136 * @param oArgs.newData {Object} New data value from form input field. 71137 * @param oArgs.oldData {Object} Old data value. 71138 */ 71139 71140 /** 71141 * Fired when a CellEditor input is saved. 71142 * 71143 * @event editorSaveEvent 71144 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71145 * @param oArgs.newData {Object} New data value from form input field. 71146 * @param oArgs.oldData {Object} Old data value. 71147 */ 71148 71149 /** 71150 * Fired when a CellEditor input is canceled. 71151 * 71152 * @event editorCancelEvent 71153 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71154 */ 71155 71156 /** 71157 * Fired when a CellEditor has a blur event. 71158 * 71159 * @event editorBlurEvent 71160 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71161 */ 71162 71163 /** 71164 * Fired when a CellEditor is blocked. 71165 * 71166 * @event editorBlockEvent 71167 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71168 */ 71169 71170 /** 71171 * Fired when a CellEditor is unblocked. 71172 * 71173 * @event editorUnblockEvent 71174 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 71175 */ 71176 71177 71178 71179 71180 71181 /** 71182 * Fired when a link is clicked. 71183 * 71184 * @event linkClickEvent 71185 * @param oArgs.event {HTMLEvent} The event object. 71186 * @param oArgs.target {HTMLElement} The A element. 71187 */ 71188 71189 /** 71190 * Fired when a BUTTON element or INPUT element of type "button", "image", 71191 * "submit", "reset" is clicked. 71192 * 71193 * @event buttonClickEvent 71194 * @param oArgs.event {HTMLEvent} The event object. 71195 * @param oArgs.target {HTMLElement} The BUTTON element. 71196 */ 71197 71198 /** 71199 * Fired when a CHECKBOX element is clicked. 71200 * 71201 * @event checkboxClickEvent 71202 * @param oArgs.event {HTMLEvent} The event object. 71203 * @param oArgs.target {HTMLElement} The CHECKBOX element. 71204 */ 71205 71206 /** 71207 * Fired when a SELECT element is changed. 71208 * 71209 * @event dropdownChangeEvent 71210 * @param oArgs.event {HTMLEvent} The event object. 71211 * @param oArgs.target {HTMLElement} The SELECT element. 71212 */ 71213 71214 /** 71215 * Fired when a RADIO element is clicked. 71216 * 71217 * @event radioClickEvent 71218 * @param oArgs.event {HTMLEvent} The event object. 71219 * @param oArgs.target {HTMLElement} The RADIO element. 71220 */ 71221 71222 71223 71224 71225 71226 71227 71228 71229 71230 71231 71232 71233 71234 71235 71236 71237 71238 71239 71240 71241 71242 71243 71244 71245 71246 71247///////////////////////////////////////////////////////////////////////////// 71248// 71249// Deprecated APIs 71250// 71251///////////////////////////////////////////////////////////////////////////// 71252 71253/* 71254 * @method showCellEditorBtns 71255 * @deprecated Use CellEditor.renderBtns() 71256 */ 71257showCellEditorBtns : function(elContainer) { 71258 // Buttons 71259 var elBtnsDiv = elContainer.appendChild(document.createElement("div")); 71260 Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON); 71261 71262 // Save button 71263 var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button")); 71264 Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT); 71265 elSaveBtn.innerHTML = "OK"; 71266 Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) { 71267 oSelf.onEventSaveCellEditor(oArgs, oSelf); 71268 oSelf.focusTbodyEl(); 71269 }, this, true); 71270 71271 // Cancel button 71272 var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button")); 71273 elCancelBtn.innerHTML = "Cancel"; 71274 Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) { 71275 oSelf.onEventCancelCellEditor(oArgs, oSelf); 71276 oSelf.focusTbodyEl(); 71277 }, this, true); 71278 71279 YAHOO.log("The method showCellEditorBtns() has been deprecated." + 71280 " Please use the CellEditor class.", "warn", this.toString()); 71281}, 71282 71283/** 71284 * @method resetCellEditor 71285 * @deprecated Use destroyCellEditor 71286 */ 71287resetCellEditor : function() { 71288 var elContainer = this._oCellEditor.container; 71289 elContainer.style.display = "none"; 71290 Ev.purgeElement(elContainer, true); 71291 elContainer.innerHTML = ""; 71292 this._oCellEditor.value = null; 71293 this._oCellEditor.isActive = false;
71294 71295 YAHOO.log("The method resetCellEditor() has been deprecated." + 71296 " Please use the CellEditor class.", "warn", this.toString()); 71297}, 71298 71299/** 71300 * @event editorUpdateEvent 71301 * @deprecated Use CellEditor class. 71302 */ 71303 71304/** 71305 * @method getBody 71306 * @deprecated Use getTbodyEl(). 71307 */ 71308getBody : function() { 71309 // Backward compatibility 71310 YAHOO.log("The method getBody() has been deprecated" + 71311 " in favor of getTbodyEl()", "warn", this.toString()); 71312 return this.getTbodyEl(); 71313}, 71314 71315/** 71316 * @method getCell 71317 * @deprecated Use getTdEl(). 71318 */ 71319getCell : function(index) { 71320 // Backward compatibility 71321 YAHOO.log("The method getCell() has been deprecated" + 71322 " in favor of getTdEl()", "warn", this.toString()); 71323 return this.getTdEl(index); 71324}, 71325 71326/** 71327 * @method getRow 71328 * @deprecated Use getTrEl(). 71329 */ 71330getRow : function(index) { 71331 // Backward compatibility 71332 YAHOO.log("The method getRow() has been deprecated" + 71333 " in favor of getTrEl()", "warn", this.toString()); 71334 return this.getTrEl(index); 71335}, 71336 71337/** 71338 * @method refreshView 71339 * @deprecated Use render. 71340 */ 71341refreshView : function() { 71342 // Backward compatibility 71343 YAHOO.log("The method refreshView() has been deprecated" + 71344 " in favor of render()", "warn", this.toString()); 71345 this.render(); 71346}, 71347 71348/** 71349 * @method select 71350 * @deprecated Use selectRow. 71351 */ 71352select : function(els) { 71353 // Backward compatibility 71354 YAHOO.log("The method select() has been deprecated" + 71355 " in favor of selectRow()", "warn", this.toString()); 71356 if(!lang.isArray(els)) { 71357 els = [els]; 71358 } 71359 for(var i=0; i<els.length; i++) { 71360 this.selectRow(els[i]); 71361 } 71362}, 71363 71364/** 71365 * @method onEventEditCell 71366 * @deprecated Use onEventShowCellEditor. 71367 */ 71368onEventEditCell : function(oArgs) { 71369 // Backward compatibility 71370 YAHOO.log("The method onEventEditCell() has been deprecated" + 71371 " in favor of onEventShowCellEditor()", "warn", this.toString()); 71372 this.onEventShowCellEditor(oArgs); 71373}, 71374 71375/** 71376 * @method _syncColWidths 71377 * @deprecated Use validateColumnWidths. 71378 */ 71379_syncColWidths : function() { 71380 // Backward compatibility 71381 YAHOO.log("The method _syncColWidths() has been deprecated" + 71382 " in favor of validateColumnWidths()", "warn", this.toString()); 71383 this.validateColumnWidths(); 71384} 71385 71386/** 71387 * @event headerRowMouseoverEvent 71388 * @deprecated Use theadRowMouseoverEvent. 71389 */ 71390 71391/** 71392 * @event headerRowMouseoutEvent 71393 * @deprecated Use theadRowMouseoutEvent. 71394 */ 71395 71396/** 71397 * @event headerRowMousedownEvent 71398 * @deprecated Use theadRowMousedownEvent. 71399 */ 71400 71401/** 71402 * @event headerRowClickEvent 71403 * @deprecated Use theadRowClickEvent. 71404 */ 71405 71406/** 71407 * @event headerRowDblclickEvent 71408 * @deprecated Use theadRowDblclickEvent. 71409 */ 71410 71411/** 71412 * @event headerCellMouseoverEvent 71413 * @deprecated Use theadCellMouseoverEvent. 71414 */ 71415 71416/** 71417 * @event headerCellMouseoutEvent 71418 * @deprecated Use theadCellMouseoutEvent. 71419 */ 71420 71421/** 71422 * @event headerCellMousedownEvent 71423 * @deprecated Use theadCellMousedownEvent. 71424 */ 71425 71426/** 71427 * @event headerCellClickEvent 71428 * @deprecated Use theadCellClickEvent. 71429 */ 71430 71431/** 71432 * @event headerCellDblclickEvent 71433 * @deprecated Use theadCellDblclickEvent. 71434 */ 71435 71436/** 71437 * @event headerLabelMouseoverEvent 71438 * @deprecated Use theadLabelMouseoverEvent. 71439 */ 71440 71441/** 71442 * @event headerLabelMouseoutEvent 71443 * @deprecated Use theadLabelMouseoutEvent. 71444 */ 71445 71446/** 71447 * @event headerLabelMousedownEvent 71448 * @deprecated Use theadLabelMousedownEvent. 71449 */ 71450 71451/** 71452 * @event headerLabelClickEvent 71453 * @deprecated Use theadLabelClickEvent. 71454 */ 71455 71456/** 71457 * @event headerLabelDbllickEvent 71458 * @deprecated Use theadLabelDblclickEvent. 71459 */ 71460 71461}); 71462 71463/** 71464 * Alias for onDataReturnSetRows for backward compatibility 71465 * @method onDataReturnSetRecords 71466 * @deprecated Use onDataReturnSetRows 71467 */ 71468DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows; 71469 71470/** 71471 * Alias for onPaginatorChange for backward compatibility 71472 * @method onPaginatorChange 71473 * @deprecated Use onPaginatorChangeRequest 71474 */ 71475DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest; 71476 71477///////////////////////////////////////////////////////////////////////////// 71478// 71479// Deprecated static APIs 71480// 71481///////////////////////////////////////////////////////////////////////////// 71482/** 71483 * @method DataTable.editCheckbox 71484 * @deprecated Use YAHOO.widget.CheckboxCellEditor. 71485 */ 71486DT.editCheckbox = function() {}; 71487 71488/** 71489 * @method DataTable.editDate 71490 * @deprecated Use YAHOO.widget.DateCellEditor. 71491 */ 71492DT.editDate = function() {}; 71493 71494/** 71495 * @method DataTable.editDropdown 71496 * @deprecated Use YAHOO.widget.DropdownCellEditor. 71497 */ 71498DT.editDropdown = function() {}; 71499 71500/** 71501 * @method DataTable.editRadio 71502 * @deprecated Use YAHOO.widget.RadioCellEditor. 71503 */ 71504DT.editRadio = function() {}; 71505 71506/** 71507 * @method DataTable.editTextarea 71508 * @deprecated Use YAHOO.widget.TextareaCellEditor 71509 */ 71510DT.editTextarea = function() {}; 71511 71512/** 71513 * @method DataTable.editTextbox 71514 * @deprecated Use YAHOO.widget.TextboxCellEditor 71515 */ 71516DT.editTextbox= function() {}; 71517 71518})(); 71519 71520(function () { 71521 71522var lang = YAHOO.lang, 71523 util = YAHOO.util, 71524 widget = YAHOO.widget, 71525 ua = YAHOO.env.ua, 71526 71527 Dom = util.Dom, 71528 Ev = util.Event, 71529 DS = util.DataSourceBase, 71530 DT = widget.DataTable, 71531 Pag = widget.Paginator; 71532 71533/** 71534 * The ScrollingDataTable class extends the DataTable class to provide 71535 * functionality for x-scrolling, y-scrolling, and xy-scrolling. 71536 * 71537 * @namespace YAHOO.widget 71538 * @class ScrollingDataTable 71539 * @extends YAHOO.widget.DataTable 71540 * @constructor 71541 * @param elContainer {HTMLElement} Container element for the TABLE. 71542 * @param aColumnDefs {Object[]} Array of object literal Column definitions. 71543 * @param oDataSource {YAHOO.util.DataSource} DataSource instance. 71544 * @param oConfigs {object} (optional) Object literal of configuration values. 71545 */ 71546widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) { 71547 oConfigs = oConfigs || {}; 71548 71549 // Prevent infinite loop 71550 if(oConfigs.scrollable) { 71551 oConfigs.scrollable = false;
71552 } 71553 71554 this._init(); 71555 71556 widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs); 71557 71558 // Once per instance 71559 this.subscribe("columnShowEvent", this._onColumnChange); 71560}; 71561 71562var SDT = widget.ScrollingDataTable; 71563 71564///////////////////////////////////////////////////////////////////////////// 71565// 71566// Public constants 71567// 71568///////////////////////////////////////////////////////////////////////////// 71569lang.augmentObject(SDT, { 71570 71571 /** 71572 * Class name assigned to inner DataTable header container. 71573 * 71574 * @property DataTable.CLASS_HEADER 71575 * @type String 71576 * @static 71577 * @final 71578 * @default "yui-dt-hd" 71579 */ 71580 CLASS_HEADER : "yui-dt-hd", 71581 71582 /** 71583 * Class name assigned to inner DataTable body container. 71584 * 71585 * @property DataTable.CLASS_BODY 71586 * @type String 71587 * @static 71588 * @final 71589 * @default "yui-dt-bd" 71590 */ 71591 CLASS_BODY : "yui-dt-bd" 71592}); 71593 71594lang.extend(SDT, DT, { 71595 71596/** 71597 * Container for fixed header TABLE element. 71598 * 71599 * @property _elHdContainer 71600 * @type HTMLElement 71601 * @private 71602 */ 71603_elHdContainer : null, 71604 71605/** 71606 * Fixed header TABLE element. 71607 * 71608 * @property _elHdTable 71609 * @type HTMLElement 71610 * @private 71611 */ 71612_elHdTable : null, 71613 71614/** 71615 * Container for scrolling body TABLE element. 71616 * 71617 * @property _elBdContainer 71618 * @type HTMLElement 71619 * @private 71620 */ 71621_elBdContainer : null, 71622 71623/** 71624 * Body THEAD element. 71625 * 71626 * @property _elBdThead 71627 * @type HTMLElement 71628 * @private 71629 */ 71630_elBdThead : null, 71631 71632/** 71633 * Offscreen container to temporarily clone SDT for auto-width calculation. 71634 * 71635 * @property _elTmpContainer 71636 * @type HTMLElement 71637 * @private 71638 */ 71639_elTmpContainer : null, 71640 71641/** 71642 * Offscreen TABLE element for auto-width calculation. 71643 * 71644 * @property _elTmpTable 71645 * @type HTMLElement 71646 * @private 71647 */ 71648_elTmpTable : null, 71649 71650/** 71651 * True if x-scrollbar is currently visible. 71652 * @property _bScrollbarX 71653 * @type Boolean 71654 * @private 71655 */ 71656_bScrollbarX : null, 71657 71658 71659 71660 71661 71662 71663 71664 71665 71666 71667 71668 71669 71670 71671 71672///////////////////////////////////////////////////////////////////////////// 71673// 71674// Superclass methods 71675// 71676///////////////////////////////////////////////////////////////////////////// 71677 71678/** 71679 * Implementation of Element's abstract method. Sets up config values. 71680 * 71681 * @method initAttributes 71682 * @param oConfigs {Object} (Optional) Object literal definition of configuration values. 71683 * @private 71684 */ 71685 71686initAttributes : function(oConfigs) { 71687 oConfigs = oConfigs || {}; 71688 SDT.superclass.initAttributes.call(this, oConfigs); 71689 71690 /** 71691 * @attribute width 71692 * @description Table width for scrollable tables (e.g., "40em").
71693 * @type String 71694 */ 71695 this.setAttributeConfig("width", { 71696 value: null, 71697 validator: lang.isString, 71698 method: function(oParam) { 71699 if(this._elHdContainer && this._elBdContainer) { 71700 this._elHdContainer.style.width = oParam; 71701 this._elBdContainer.style.width = oParam; 71702 this._syncScrollX(); 71703 this._syncScrollOverhang(); 71704 } 71705 } 71706 }); 71707 71708 /** 71709 * @attribute height 71710 * @description Table body height for scrollable tables, not including headers (e.g., "40em"). 71711 * @type String 71712 */ 71713 this.setAttributeConfig("height", { 71714 value: null, 71715 validator: lang.isString, 71716 method: function(oParam) { 71717 if(this._elHdContainer && this._elBdContainer) { 71718 this._elBdContainer.style.height = oParam; 71719 this._syncScrollX(); 71720 this._syncScrollY(); 71721 this._syncScrollOverhang(); 71722 } 71723 } 71724 }); 71725 71726 /** 71727 * @attribute COLOR_COLUMNFILLER
71728 * @description CSS color value assigned to header filler on scrollable tables. 71729 * @type String 71730 * @default "#F2F2F2" 71731 */ 71732 this.setAttributeConfig("COLOR_COLUMNFILLER", { 71733 value: "#F2F2F2", 71734 validator: lang.isString, 71735 method: function(oParam) { 71736 if(this._elHdContainer) { 71737 this._elHdContainer.style.backgroundColor = oParam; 71738 } 71739 } 71740 }); 71741}, 71742 71743/** 71744 * Initializes internal variables. 71745 * 71746 * @method _init 71747 * @private 71748 */ 71749_init : function() { 71750 this._elHdContainer = null; 71751 this._elHdTable = null; 71752 this._elBdContainer = null; 71753 this._elBdThead = null; 71754 this._elTmpContainer = null; 71755 this._elTmpTable = null; 71756}, 71757 71758/** 71759 * Initializes DOM elements for a ScrollingDataTable, including creation of 71760 * two separate TABLE elements. 71761 * 71762 * @method _initDomElements 71763 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 71764 * return {Boolean} False in case of error, otherwise true 71765 * @private 71766 */ 71767_initDomElements : function(elContainer) { 71768 // Outer and inner containers 71769 this._initContainerEl(elContainer); 71770 if(this._elContainer && this._elHdContainer && this._elBdContainer) { 71771 // TABLEs 71772 this._initTableEl(); 71773 71774 if(this._elHdTable && this._elTable) { 71775 // COLGROUPs 71776 ///this._initColgroupEl(this._elHdTable, this._elTable); 71777 this._initColgroupEl(this._elHdTable); 71778 71779 // THEADs 71780 this._initTheadEl(this._elHdTable, this._elTable); 71781 71782 // Primary TBODY 71783 this._initTbodyEl(this._elTable); 71784 // Message TBODY 71785 this._initMsgTbodyEl(this._elTable); 71786 } 71787 } 71788 if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody || 71789 !this._elHdTable || !this._elBdThead) { 71790 YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString()); 71791 return false; 71792 } 71793 else { 71794 return true; 71795 } 71796}, 71797 71798/** 71799 * Destroy's the DataTable outer and inner container elements, if available. 71800 * 71801 * @method _destroyContainerEl 71802 * @param elContainer {HTMLElement} Reference to the container element. 71803 * @private 71804 */ 71805_destroyContainerEl : function(elContainer) { 71806 Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE); 71807 SDT.superclass._destroyContainerEl.call(this, elContainer); 71808 this._elHdContainer = null; 71809 this._elBdContainer = null; 71810}, 71811 71812/** 71813 * Initializes the DataTable outer container element and creates inner header 71814 * and body container elements. 71815 * 71816 * @method _initContainerEl 71817 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 71818 * @private 71819 */ 71820_initContainerEl : function(elContainer) { 71821 SDT.superclass._initContainerEl.call(this, elContainer); 71822 71823 if(this._elContainer) { 71824 elContainer = this._elContainer; // was constructor input, now is DOM ref 71825 Dom.addClass(elContainer, DT.CLASS_SCROLLABLE); 71826 71827 // Container for header TABLE 71828 var elHdContainer = document.createElement("div"); 71829 elHdContainer.style.width = this.get("width") || ""; 71830 elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER"); 71831 Dom.addClass(elHdContainer, SDT.CLASS_HEADER); 71832 this._elHdContainer = elHdContainer; 71833 elContainer.appendChild(elHdContainer); 71834 71835 // Container for body TABLE 71836 var elBdContainer = document.createElement("div"); 71837 elBdContainer.style.width = this.get("width") || ""; 71838 elBdContainer.style.height = this.get("height") || ""; 71839 Dom.addClass(elBdContainer, SDT.CLASS_BODY); 71840 Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers 71841 this._elBdContainer = elBdContainer; 71842 elContainer.appendChild(elBdContainer); 71843 } 71844}, 71845 71846/** 71847 * Creates HTML markup CAPTION element. 71848 * 71849 * @method _initCaptionEl 71850 * @param sCaption {String} Text for caption. 71851 * @private 71852 */ 71853_initCaptionEl : function(sCaption) { 71854 // Not yet supported 71855 /*if(this._elHdTable && sCaption) { 71856 // Create CAPTION element 71857 if(!this._elCaption) { 71858 this._elCaption = this._elHdTable.createCaption(); 71859 } 71860 // Set CAPTION value 71861 this._elCaption.innerHTML = sCaption; 71862 } 71863 else if(this._elCaption) { 71864 this._elCaption.parentNode.removeChild(this._elCaption); 71865 }*/ 71866}, 71867 71868/** 71869 * Destroy's the DataTable head TABLE element, if available. 71870 * 71871 * @method _destroyHdTableEl 71872 * @private 71873 */ 71874_destroyHdTableEl : function() { 71875 var elTable = this._elHdTable; 71876 if(elTable) { 71877 Ev.purgeElement(elTable, true); 71878 elTable.parentNode.removeChild(elTable); 71879 71880 // A little out of place, but where else can we null out these extra elements? 71881 ///this._elBdColgroup = null; 71882 this._elBdThead = null; 71883 } 71884}, 71885 71886/** 71887 * Initializes ScrollingDataTable TABLE elements into the two inner containers. 71888 * 71889 * @method _initTableEl 71890 * @private 71891 */ 71892_initTableEl : function() { 71893 // Head TABLE 71894 if(this._elHdContainer) { 71895 this._destroyHdTableEl(); 71896 71897 // Create TABLE 71898 this._elHdTable = this._elHdContainer.appendChild(document.createElement("table")); 71899 71900 // Set up mouseover/mouseout events via mouseenter/mouseleave delegation 71901 Ev.delegate(this._elHdTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this); 71902 Ev.delegate(this._elHdTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this); 71903 } 71904 // Body TABLE 71905 SDT.superclass._initTableEl.call(this, this._elBdContainer); 71906}, 71907 71908/** 71909 * Initializes ScrollingDataTable THEAD elements into the two inner containers. 71910 * 71911 * @method _initTheadEl 71912 * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference. 71913 * @param elTable {HTMLElement} (optional) TABLE element reference. 71914 * @private 71915 */ 71916_initTheadEl : function(elHdTable, elTable) { 71917 elHdTable = elHdTable || this._elHdTable; 71918 elTable = elTable || this._elTable; 71919 71920 // Scrolling body's THEAD 71921 this._initBdTheadEl(elTable); 71922 // Standard fixed head THEAD 71923 SDT.superclass._initTheadEl.call(this, elHdTable); 71924}, 71925 71926/** 71927 * SDT changes ID so as not to duplicate the accessibility TH IDs. 71928 * 71929 * @method _initThEl 71930 * @param elTh {HTMLElement} TH element reference. 71931 * @param oColumn {YAHOO.widget.Column} Column object. 71932 * @private 71933 */ 71934_initThEl : function(elTh, oColumn) { 71935 SDT.superclass._initThEl.call(this, elTh, oColumn); 71936 elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD 71937}, 71938 71939/** 71940 * Destroy's the DataTable body THEAD element, if available. 71941 * 71942 * @method _destroyBdTheadEl 71943 * @private 71944 */ 71945_destroyBdTheadEl : function() { 71946 var elBdThead = this._elBdThead; 71947 if(elBdThead) { 71948 var elTable = elBdThead.parentNode; 71949 Ev.purgeElement(elBdThead, true); 71950 elTable.removeChild(elBdThead); 71951 this._elBdThead = null; 71952 71953 this._destroyColumnHelpers(); 71954 } 71955}, 71956 71957/** 71958 * Initializes body THEAD element. 71959 * 71960 * @method _initBdTheadEl 71961 * @param elTable {HTMLElement} TABLE element into which to create THEAD. 71962 * @return {HTMLElement} Initialized THEAD element. 71963 * @private 71964 */ 71965_initBdTheadEl : function(elTable) { 71966 if(elTable) { 71967 // Destroy previous 71968 this._destroyBdTheadEl(); 71969 71970 var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild); 71971 71972 // Add TRs to the THEAD; 71973 var oColumnSet = this._oColumnSet, 71974 colTree = oColumnSet.tree, 71975 elTh, elTheadTr, oColumn, i, j, k, len; 71976 71977 for(i=0, k=colTree.length; i<k; i++) { 71978 elTheadTr = elThead.appendChild(document.createElement("tr")); 71979 71980 // ...and create TH cells 71981 for(j=0, len=colTree[i].length; j<len; j++) { 71982 oColumn = colTree[i][j]; 71983 elTh = elTheadTr.appendChild(document.createElement("th")); 71984 this._initBdThEl(elTh,oColumn,i,j); 71985 } 71986 } 71987 this._elBdThead = elThead; 71988 YAHOO.log("Accessibility TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString()); 71989 } 71990}, 71991 71992/** 71993 * Populates TH element for the body THEAD element. 71994 * 71995 * @method _initBdThEl 71996 * @param elTh {HTMLElement} TH element reference. 71997 * @param oColumn {YAHOO.widget.Column} Column object. 71998 * @private 71999 */ 72000_initBdThEl : function(elTh, oColumn) { 72001 elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility 72002 elTh.rowSpan = oColumn.getRowspan(); 72003 elTh.colSpan = oColumn.getColspan(); 72004 // Assign abbr attribute 72005 if(oColumn.abbr) { 72006 elTh.abbr = oColumn.abbr; 72007 } 72008 72009 // TODO: strip links and form elements 72010 var sKey = oColumn.getKey(); 72011 var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey; 72012 elTh.innerHTML = sLabel; 72013}, 72014 72015/** 72016 * Initializes ScrollingDataTable TBODY element for data 72017 * 72018 * @method _initTbodyEl 72019 * @param elTable {HTMLElement} TABLE element into which to create TBODY . 72020 * @private 72021 */ 72022_initTbodyEl : function(elTable) { 72023 SDT.superclass._initTbodyEl.call(this, elTable); 72024 72025 // Bug 2105534 - Safari 3 gap 72026 // Bug 2492591 - IE8 offsetTop 72027 elTable.style.marginTop = (this._elTbody.offsetTop > 0) ? 72028 "-"+this._elTbody.offsetTop+"px" : 0; 72029}, 72030 72031 72032 72033 72034 72035 72036 72037 72038 72039 72040 72041 72042 72043 72044 72045 72046 72047 72048 72049 72050 72051 72052 72053 72054 72055 72056 72057 72058 72059/** 72060 * Sets focus on the given element. 72061 * 72062 * @method _focusEl 72063 * @param el {HTMLElement} Element. 72064 * @private 72065 */ 72066_focusEl : function(el) { 72067 el = el || this._elTbody; 72068 var oSelf = this; 72069 this._storeScrollPositions(); 72070 // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets 72071 // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing 72072 // strange unexpected things as the user clicks on buttons and other controls. 72073 72074 // Bug 1921135: Wrap the whole thing in a setTimeout 72075 setTimeout(function() { 72076 setTimeout(function() { 72077 try { 72078 el.focus(); 72079 oSelf._restoreScrollPositions(); 72080 } 72081 catch(e) { 72082 } 72083 },0); 72084 }, 0); 72085}, 72086 72087 72088 72089 72090 72091 72092 72093 72094 72095 72096 72097 72098 72099 72100 72101 72102 72103 72104 72105/** 72106 * Internal wrapper calls run() on render Chain instance. 72107 * 72108 * @method _runRenderChain 72109 * @private 72110 */ 72111_runRenderChain : function() { 72112 this._storeScrollPositions(); 72113 this._oChainRender.run(); 72114}, 72115 72116/** 72117 * Stores scroll positions so they can be restored after a render. 72118 * 72119 * @method _storeScrollPositions 72120 * @private 72121 */ 72122 _storeScrollPositions : function() { 72123 this._nScrollTop = this._elBdContainer.scrollTop; 72124 this._nScrollLeft = this._elBdContainer.scrollLeft; 72125}, 72126 72127/** 72128 * Clears stored scroll positions to interrupt the automatic restore mechanism. 72129 * Useful for setting scroll positions programmatically rather than as part of 72130 * the post-render cleanup process. 72131 * 72132 * @method clearScrollPositions 72133 * @private 72134 */ 72135 clearScrollPositions : function() { 72136 this._nScrollTop = 0; 72137 this._nScrollLeft = 0; 72138}, 72139 72140/** 72141 * Restores scroll positions to stored value. 72142 * 72143 * @method _retoreScrollPositions 72144 * @private 72145 */ 72146 _restoreScrollPositions : function() { 72147 // Reset scroll positions 72148 if(this._nScrollTop) { 72149 this._elBdContainer.scrollTop = this._nScrollTop; 72150 this._nScrollTop = null; 72151 } 72152 if(this._nScrollLeft) { 72153 this._elBdContainer.scrollLeft = this._nScrollLeft; 72154 // Bug 2529024 72155 this._elHdContainer.scrollLeft = this._nScrollLeft; 72156 this._nScrollLeft = null; 72157 } 72158}, 72159 72160/** 72161 * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable. 72162 * 72163 * @method _validateColumnWidth 72164 * @param oColumn {YAHOO.widget.Column} Column instance. 72165 * @param elTd {HTMLElement} TD element to validate against. 72166 * @private 72167 */ 72168_validateColumnWidth : function(oColumn, elTd) { 72169 // Only Columns without widths that are not hidden 72170 if(!oColumn.width && !oColumn.hidden) { 72171 var elTh = oColumn.getThEl(); 72172 // Unset a calculated auto-width 72173 if(oColumn._calculatedWidth) { 72174 this._setColumnWidth(oColumn, "auto", "visible"); 72175 } 72176 // Compare auto-widths 72177 if(elTh.offsetWidth !== elTd.offsetWidth) { 72178 var elWider = (elTh.offsetWidth > elTd.offsetWidth) ? 72179 oColumn.getThLinerEl() : elTd.firstChild; 72180 72181 // Grab the wider liner width, unless the minWidth is wider 72182 var newWidth = Math.max(0, 72183 (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)), 72184 oColumn.minWidth); 72185 72186 var sOverflow = 'visible'; 72187 72188 // Now validate against maxAutoWidth 72189 if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) { 72190 newWidth = oColumn.maxAutoWidth; 72191 sOverflow = "hidden"; 72192 } 72193 72194 // Set to the wider auto-width 72195 this._elTbody.style.display = "none"; 72196 this._setColumnWidth(oColumn, newWidth+'px', sOverflow); 72197 oColumn._calculatedWidth = newWidth; 72198 this._elTbody.style.display = ""; 72199 } 72200 } 72201}, 72202 72203/** 72204 * For one or all Columns of a ScrollingDataTable, when Column is not hidden, 72205 * and width is not set, syncs widths of header and body cells and 72206 * validates that width against minWidth and/or maxAutoWidth as necessary. 72207 * 72208 * @method validateColumnWidths 72209 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated. 72210 */ 72211validateColumnWidths : function(oColumn) { 72212 // Validate there is at least one TR with proper TDs 72213 var allKeys = this._oColumnSet.keys,
72214 allKeysLength = allKeys.length, 72215 elRow = this.getFirstTrEl(); 72216 72217 // Reset overhang for IE 72218 if(ua.ie) { 72219 this._setOverhangValue(1); 72220 } 72221 72222 if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) { 72223 // Temporarily unsnap container since it causes inaccurate calculations 72224 var sWidth = this.get("width"); 72225 if(sWidth) { 72226 this._elHdContainer.style.width = ""; 72227 this._elBdContainer.style.width = ""; 72228 } 72229 this._elContainer.style.width = ""; 72230 72231 //Validate just one Column 72232 if(oColumn && lang.isNumber(oColumn.getKeyIndex())) { 72233 this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]); 72234 } 72235 // Iterate through all Columns to unset calculated widths in one pass 72236 else { 72237 var elTd, todos = [], thisTodo, i, len; 72238 for(i=0; i<allKeysLength; i++) { 72239 oColumn = allKeys[i]; 72240 // Only Columns without widths that are not hidden, unset a calculated auto-width 72241 if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) { 72242 todos[todos.length] = oColumn; 72243 } 72244 } 72245 72246 this._elTbody.style.display = "none"; 72247 for(i=0, len=todos.length; i<len; i++) { 72248 this._setColumnWidth(todos[i], "auto", "visible"); 72249 } 72250 this._elTbody.style.display = ""; 72251 72252 todos = []; 72253 72254 // Iterate through all Columns and make the store the adjustments to make in one pass 72255 for(i=0; i<allKeysLength; i++) { 72256 oColumn = allKeys[i]; 72257 elTd = elRow.childNodes[i]; 72258 // Only Columns without widths that are not hidden 72259 if(!oColumn.width && !oColumn.hidden) { 72260 var elTh = oColumn.getThEl(); 72261 72262 // Compare auto-widths 72263 if(elTh.offsetWidth !== elTd.offsetWidth) { 72264 var elWider = (elTh.offsetWidth > elTd.offsetWidth) ? 72265 oColumn.getThLinerEl() : elTd.firstChild; 72266 72267 // Grab the wider liner width, unless the minWidth is wider 72268 var newWidth = Math.max(0, 72269 (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)), 72270 oColumn.minWidth); 72271 72272 var sOverflow = 'visible'; 72273 72274 // Now validate against maxAutoWidth 72275 if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) { 72276 newWidth = oColumn.maxAutoWidth; 72277 sOverflow = "hidden"; 72278 } 72279 72280 todos[todos.length] = [oColumn, newWidth, sOverflow]; 72281 } 72282 } 72283 } 72284 72285 this._elTbody.style.display = "none"; 72286 for(i=0, len=todos.length; i<len; i++) { 72287 thisTodo = todos[i]; 72288 // Set to the wider auto-width 72289 this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]); 72290 thisTodo[0]._calculatedWidth = thisTodo[1]; 72291 } 72292 this._elTbody.style.display = ""; 72293 } 72294 72295 // Resnap unsnapped containers 72296 if(sWidth) { 72297 this._elHdContainer.style.width = sWidth; 72298 this._elBdContainer.style.width = sWidth; 72299 } 72300 } 72301 72302 this._syncScroll(); 72303 this._restoreScrollPositions(); 72304}, 72305 72306/** 72307 * Syncs padding around scrollable tables, including Column header right-padding 72308 * and container width and height. 72309 * 72310 * @method _syncScroll 72311 * @private 72312 */ 72313_syncScroll : function() { 72314 this._syncScrollX(); 72315 this._syncScrollY(); 72316 this._syncScrollOverhang(); 72317 if(ua.opera) { 72318 // Bug 1925874 72319 this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft; 72320 if(!this.get("width")) { 72321 // Bug 1926125 72322 document.body.style += ''; 72323 } 72324 } 72325 }, 72326 72327/** 72328 * Snaps container width for y-scrolling tables. 72329 * 72330 * @method _syncScrollY 72331 * @private 72332 */ 72333_syncScrollY : function() { 72334 var elTbody = this._elTbody, 72335 elBdContainer = this._elBdContainer; 72336 72337 // X-scrolling not enabled 72338 if(!this.get("width")) { 72339 // Snap outer container width to content 72340 this._elContainer.style.width = 72341 (elBdContainer.scrollHeight > elBdContainer.clientHeight) ? 72342 // but account for y-scrollbar since it is visible 72343 (elTbody.parentNode.clientWidth + 19) + "px" : 72344 // no y-scrollbar, just borders 72345 (elTbody.parentNode.clientWidth + 2) + "px"; 72346 } 72347}, 72348 72349/** 72350 * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width. 72351 * 72352 * @method _syncScrollX 72353 * @private 72354 */ 72355_syncScrollX : function() { 72356 var elTbody = this._elTbody, 72357 elBdContainer = this._elBdContainer; 72358 72359 // IE 6 and 7 only when y-scrolling not enabled 72360 if(!this.get("height") && (ua.ie)) { 72361 // Snap outer container height to content 72362 elBdContainer.style.height = 72363 // but account for x-scrollbar if it is visible 72364 (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ? 72365 (elTbody.parentNode.offsetHeight + 18) + "px" : 72366 elTbody.parentNode.offsetHeight + "px"; 72367 } 72368 72369 // Sync message tbody 72370 if(this._elTbody.rows.length === 0) { 72371 this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px"; 72372 } 72373 else { 72374 this._elMsgTbody.parentNode.style.width = ""; 72375 } 72376}, 72377 72378/** 72379 * Adds/removes Column header overhang as necesary. 72380 * 72381 * @method _syncScrollOverhang 72382 * @private 72383 */ 72384_syncScrollOverhang : function() { 72385 var elBdContainer = this._elBdContainer, 72386 // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table 72387 nPadding = 1; 72388 72389 // Y-scrollbar is visible, which is when the overhang needs to jut out 72390 if((elBdContainer.scrollHeight > elBdContainer.clientHeight) && 72391 // X-scrollbar is also visible, which means the right is jagged, not flush with the Column 72392 (elBdContainer.scrollWidth > elBdContainer.clientWidth)) { 72393 nPadding = 18; 72394 } 72395 72396 this._setOverhangValue(nPadding); 72397 72398}, 72399 72400/** 72401 * Sets Column header overhang to given width. 72402 * 72403 * @method _setOverhangValue 72404 * @param nBorderWidth {Number} Value of new border for overhang. 72405 * @private 72406 */ 72407_setOverhangValue : function(nBorderWidth) { 72408 var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [], 72409 len = aLastHeaders.length, 72410 sPrefix = this._sId+"-fixedth-", 72411 sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER"); 72412 72413 this._elThead.style.display = "none"; 72414 for(var i=0; i<len; i++) { 72415 Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue; 72416 } 72417 this._elThead.style.display = ""; 72418}, 72419 72420 72421 72422 72423 72424 72425 72426 72427 72428 72429 72430 72431 72432 72433 72434 72435 72436 72437 72438 72439 72440 72441 72442 72443 72444 72445 72446 72447 72448 72449 72450 72451 72452 72453 72454 72455 72456 72457/** 72458 * Returns DOM reference to the DataTable's fixed header container element. 72459 * 72460 * @method getHdContainerEl 72461 * @return {HTMLElement} Reference to DIV element. 72462 */ 72463getHdContainerEl : function() { 72464 return this._elHdContainer; 72465}, 72466 72467/** 72468 * Returns DOM reference to the DataTable's scrolling body container element. 72469 * 72470 * @method getBdContainerEl 72471 * @return {HTMLElement} Reference to DIV element. 72472 */ 72473getBdContainerEl : function() { 72474 return this._elBdContainer; 72475}, 72476 72477/** 72478 * Returns DOM reference to the DataTable's fixed header TABLE element. 72479 * 72480 * @method getHdTableEl 72481 * @return {HTMLElement} Reference to TABLE element. 72482 */ 72483getHdTableEl : function() { 72484 return this._elHdTable; 72485}, 72486 72487/** 72488 * Returns DOM reference to the DataTable's scrolling body TABLE element. 72489 * 72490 * @method getBdTableEl 72491 * @return {HTMLElement} Reference to TABLE element. 72492 */ 72493getBdTableEl : function() { 72494 return this._elTable; 72495}, 72496 72497/** 72498 * Disables ScrollingDataTable UI. 72499 * 72500 * @method disable 72501 */ 72502disable : function() { 72503 var elMask = this._elMask; 72504 elMask.style.width = this._elBdContainer.offsetWidth + "px"; 72505 elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px"; 72506 elMask.style.display = ""; 72507 this.fireEvent("disableEvent"); 72508}, 72509 72510/** 72511 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove 72512 * non-nested Columns, and top-level parent Columns (which will remove all 72513 * children Columns). 72514 * 72515 * @method removeColumn 72516 * @param oColumn {YAHOO.widget.Column} Column instance. 72517 * @return oColumn {YAHOO.widget.Column} Removed Column instance. 72518 */ 72519removeColumn : function(oColumn) { 72520 // Store scroll pos 72521 var hdPos = this._elHdContainer.scrollLeft; 72522 var bdPos = this._elBdContainer.scrollLeft; 72523 72524 // Call superclass method 72525 oColumn = SDT.superclass.removeColumn.call(this, oColumn); 72526 72527 // Restore scroll pos 72528 this._elHdContainer.scrollLeft = hdPos; 72529 this._elBdContainer.scrollLeft = bdPos; 72530 72531 return oColumn; 72532}, 72533 72534/** 72535 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
72536 * can only add non-nested Columns and top-level parent Columns. You cannot add 72537 * a nested Column to an existing parent. 72538 * 72539 * @method insertColumn 72540 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column 72541 * definition or a Column instance. 72542 * @param index {Number} (optional) New tree index. 72543 * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 72544 */ 72545insertColumn : function(oColumn, index) { 72546 // Store scroll pos 72547 var hdPos = this._elHdContainer.scrollLeft; 72548 var bdPos = this._elBdContainer.scrollLeft; 72549 72550 // Call superclass method 72551 var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index); 72552 72553 // Restore scroll pos 72554 this._elHdContainer.scrollLeft = hdPos; 72555 this._elBdContainer.scrollLeft = bdPos; 72556 72557 return oNewColumn; 72558}, 72559 72560/** 72561 * Removes given Column and inserts into given tree index. NOTE: You 72562 * can only reorder non-nested Columns and top-level parent Columns. You cannot 72563 * reorder a nested Column to an existing parent. 72564 * 72565 * @method reorderColumn 72566 * @param oColumn {YAHOO.widget.Column} Column instance. 72567 * @param index {Number} New tree index. 72568 */ 72569reorderColumn : function(oColumn, index) { 72570 // Store scroll pos 72571 var hdPos = this._elHdContainer.scrollLeft; 72572 var bdPos = this._elBdContainer.scrollLeft; 72573 72574 // Call superclass method 72575 var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index); 72576 72577 // Restore scroll pos 72578 this._elHdContainer.scrollLeft = hdPos; 72579 this._elBdContainer.scrollLeft = bdPos; 72580 72581 return oNewColumn; 72582}, 72583 72584/** 72585 * Sets given Column to given pixel width. If new width is less than minWidth 72586 * width, sets to minWidth. Updates oColumn.width value. 72587 * 72588 * @method setColumnWidth 72589 * @param oColumn {YAHOO.widget.Column} Column instance. 72590 * @param nWidth {Number} New width in pixels. 72591 */ 72592setColumnWidth : function(oColumn, nWidth) { 72593 oColumn = this.getColumn(oColumn); 72594 if(oColumn) { 72595 this._storeScrollPositions(); 72596 72597 // Validate new width against minWidth 72598 if(lang.isNumber(nWidth)) { 72599 nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth; 72600 72601 // Save state 72602 oColumn.width = nWidth; 72603 72604 // Resize the DOM elements 72605 this._setColumnWidth(oColumn, nWidth+"px"); 72606 this._syncScroll(); 72607 72608 this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth}); 72609 YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString()); 72610 } 72611 // Unsets a width to auto-size 72612 else if(nWidth === null) { 72613 // Save state 72614 oColumn.width = nWidth; 72615 72616 // Resize the DOM elements 72617 this._setColumnWidth(oColumn, "auto"); 72618 this.validateColumnWidths(oColumn); 72619 this.fireEvent("columnUnsetWidthEvent",{column:oColumn}); 72620 YAHOO.log("Column " + oColumn + " width unset", "info", this.toString()); 72621 } 72622 72623 // Bug 2339454: resize then sort misaligment 72624 this._clearTrTemplateEl(); 72625 } 72626 else { 72627 YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString()); 72628 } 72629}, 72630 72631/** 72632 * Scrolls to given row or cell 72633 * 72634 * @method scrollTo 72635 * @param to {YAHOO.widget.Record | HTMLElement } Itme to scroll to. 72636 */ 72637scrollTo : function(to) { 72638 var td = this.getTdEl(to); 72639 if(td) { 72640 this.clearScrollPositions(); 72641 this.getBdContainerEl().scrollLeft = td.offsetLeft; 72642 this.getBdContainerEl().scrollTop = td.parentNode.offsetTop; 72643 } 72644 else { 72645 var tr = this.getTrEl(to); 72646 if(tr) { 72647 this.clearScrollPositions(); 72648 this.getBdContainerEl().scrollTop = tr.offsetTop; 72649 } 72650 } 72651}, 72652 72653/** 72654 * Displays message within secondary TBODY. 72655 * 72656 * @method showTableMessage 72657 * @param sHTML {String} (optional) Value for innerHTMlang. 72658 * @param sClassName {String} (optional) Classname. 72659 */ 72660showTableMessage : function(sHTML, sClassName) { 72661 var elCell = this._elMsgTd; 72662 if(lang.isString(sHTML)) { 72663 elCell.firstChild.innerHTML = sHTML; 72664 } 72665 if(lang.isString(sClassName)) { 72666 Dom.addClass(elCell.firstChild, sClassName); 72667 } 72668 72669 // Needed for SDT only 72670 var elThead = this.getTheadEl(); 72671 var elTable = elThead.parentNode; 72672 var newWidth = elTable.offsetWidth; 72673 this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px"; 72674 72675 this._elMsgTbody.style.display = ""; 72676 72677 this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName}); 72678 YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString()); 72679}, 72680 72681 72682 72683 72684 72685 72686 72687 72688 72689 72690 72691 72692 72693///////////////////////////////////////////////////////////////////////////// 72694// 72695// Private Custom Event Handlers 72696// 72697///////////////////////////////////////////////////////////////////////////// 72698 72699/** 72700 * Handles Column mutations 72701 * 72702 * @method onColumnChange 72703 * @param oArgs {Object} Custom Event data. 72704 */ 72705_onColumnChange : function(oArg) { 72706 // Figure out which Column changed 72707 var oColumn = (oArg.column) ? oArg.column : 72708 (oArg.editor) ? oArg.editor.column : null; 72709 this._storeScrollPositions(); 72710 this.validateColumnWidths(oColumn); 72711}, 72712 72713 72714 72715 72716 72717 72718 72719 72720 72721 72722 72723 72724 72725 72726 72727///////////////////////////////////////////////////////////////////////////// 72728// 72729// Private DOM Event Handlers 72730// 72731///////////////////////////////////////////////////////////////////////////// 72732 72733/** 72734 * Syncs scrolltop and scrollleft of all TABLEs. 72735 * 72736 * @method _onScroll 72737 * @param e {HTMLEvent} The scroll event. 72738 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance. 72739 * @private 72740 */ 72741_onScroll : function(e, oSelf) { 72742 oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft; 72743 72744 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) { 72745 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); 72746 oSelf.cancelCellEditor(); 72747 } 72748 72749 var elTarget = Ev.getTarget(e); 72750 var elTag = elTarget.nodeName.toLowerCase(); 72751 oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget}); 72752}, 72753 72754/** 72755 * Handles keydown events on the THEAD element. 72756 * 72757 * @method _onTheadKeydown 72758 * @param e {HTMLEvent} The key event. 72759 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance. 72760 * @private 72761 */ 72762_onTheadKeydown : function(e, oSelf) { 72763 // If tabbing to next TH label link causes THEAD to scroll, 72764 // need to sync scrollLeft with TBODY 72765 if(Ev.getCharCode(e) === 9) { 72766 setTimeout(function() { 72767 if((oSelf instanceof SDT) && oSelf._sId) { 72768 oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft; 72769 } 72770 },0); 72771 } 72772 72773 var elTarget = Ev.getTarget(e); 72774 var elTag = elTarget.nodeName.toLowerCase(); 72775 var bKeepBubbling = true; 72776 while(elTarget && (elTag != "table")) { 72777 switch(elTag) { 72778 case "body": 72779 return; 72780 case "input": 72781 case "textarea": 72782 // TODO: implement textareaKeyEvent 72783 break; 72784 case "thead": 72785 bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e}); 72786 break; 72787 default: 72788 break; 72789 } 72790 if(bKeepBubbling === false) { 72791 return; 72792 } 72793 else { 72794 elTarget = elTarget.parentNode; 72795 if(elTarget) { 72796 elTag = elTarget.nodeName.toLowerCase(); 72797 } 72798 } 72799 } 72800 oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e}); 72801} 72802 72803 72804 72805 72806/** 72807 * Fired when a fixed scrolling DataTable has a scroll. 72808 * 72809 * @event tableScrollEvent 72810 * @param oArgs.event {HTMLEvent} The event object. 72811 * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE) 72812 * or the DataTable's TBODY element (everyone else). 72813 * 72814 */ 72815 72816 72817 72818 72819}); 72820 72821})(); 72822 72823(function () { 72824 72825var lang = YAHOO.lang, 72826 util = YAHOO.util, 72827 widget = YAHOO.widget, 72828 ua = YAHOO.env.ua, 72829 72830 Dom = util.Dom, 72831 Ev = util.Event, 72832 72833 DT = widget.DataTable; 72834/****************************************************************************/ 72835/****************************************************************************/ 72836/****************************************************************************/ 72837 72838/** 72839 * The BaseCellEditor class provides base functionality common to all inline cell 72840 * editors for a DataTable widget. 72841 * 72842 * @namespace YAHOO.widget 72843 * @class BaseCellEditor 72844 * @uses YAHOO.util.EventProvider 72845 * @constructor 72846 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors. 72847 * @param oConfigs {Object} (Optional) Object literal of configs. 72848 */ 72849widget.BaseCellEditor = function(sType, oConfigs) { 72850 this._sId = this._sId || Dom.generateId(null, "yui-ceditor"); // "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++; 72851 YAHOO.widget.BaseCellEditor._nCount++; 72852 this._sType = sType; 72853 72854 // Validate inputs 72855 this._initConfigs(oConfigs); 72856 72857 // Create Custom Events 72858 this._initEvents(); 72859 72860 // UI needs to be drawn 72861 this._needsRender = true; 72862}; 72863 72864var BCE = widget.BaseCellEditor; 72865 72866///////////////////////////////////////////////////////////////////////////// 72867// 72868// Static members 72869// 72870///////////////////////////////////////////////////////////////////////////// 72871lang.augmentObject(BCE, { 72872 72873/** 72874 * Global instance counter. 72875 * 72876 * @property CellEditor._nCount 72877 * @type Number 72878 * @static 72879 * @default 0 72880 * @private 72881 */ 72882_nCount : 0, 72883 72884/** 72885 * Class applied to CellEditor container. 72886 * 72887 * @property CellEditor.CLASS_CELLEDITOR
72888 * @type String 72889 * @static 72890 * @default "yui-ceditor" 72891 */ 72892CLASS_CELLEDITOR : "yui-ceditor" 72893 72894}); 72895 72896BCE.prototype = { 72897///////////////////////////////////////////////////////////////////////////// 72898// 72899// Private members 72900// 72901///////////////////////////////////////////////////////////////////////////// 72902/** 72903 * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique 72904 * DOM ID strings and log messages. 72905 * 72906 * @property _sId 72907 * @type String 72908 * @private 72909 */ 72910_sId : null, 72911 72912/** 72913 * Editor type. 72914 * 72915 * @property _sType 72916 * @type String 72917 * @private 72918 */ 72919_sType : null, 72920 72921/** 72922 * DataTable instance. 72923 * 72924 * @property _oDataTable 72925 * @type YAHOO.widget.DataTable 72926 * @private 72927 */ 72928_oDataTable : null, 72929 72930/** 72931 * Column instance. 72932 * 72933 * @property _oColumn 72934 * @type YAHOO.widget.Column 72935 * @default null 72936 * @private 72937 */ 72938_oColumn : null, 72939 72940/** 72941 * Record instance. 72942 * 72943 * @property _oRecord 72944 * @type YAHOO.widget.Record 72945 * @default null 72946 * @private 72947 */ 72948_oRecord : null, 72949 72950/** 72951 * TD element. 72952 * 72953 * @property _elTd 72954 * @type HTMLElement 72955 * @default null 72956 * @private 72957 */ 72958_elTd : null, 72959 72960/** 72961 * Container for inline editor. 72962 * 72963 * @property _elContainer 72964 * @type HTMLElement 72965 * @private 72966 */ 72967_elContainer : null, 72968 72969/** 72970 * Reference to Cancel button, if available. 72971 * 72972 * @property _elCancelBtn 72973 * @type HTMLElement 72974 * @default null 72975 * @private 72976 */ 72977_elCancelBtn : null, 72978 72979/** 72980 * Reference to Save button, if available. 72981 * 72982 * @property _elSaveBtn 72983 * @type HTMLElement 72984 * @default null 72985 * @private 72986 */ 72987_elSaveBtn : null, 72988 72989 72990 72991 72992 72993 72994 72995 72996///////////////////////////////////////////////////////////////////////////// 72997// 72998// Private methods 72999// 73000///////////////////////////////////////////////////////////////////////////// 73001 73002/** 73003 * Initialize configs. 73004 * 73005 * @method _initConfigs 73006 * @private 73007 */ 73008_initConfigs : function(oConfigs) { 73009 // Object literal defines CellEditor configs 73010 if(oConfigs && YAHOO.lang.isObject(oConfigs)) { 73011 for(var sConfig in oConfigs) { 73012 if(sConfig) { 73013 this[sConfig] = oConfigs[sConfig]; 73014 } 73015 } 73016 } 73017}, 73018 73019/** 73020 * Initialize Custom Events. 73021 * 73022 * @method _initEvents 73023 * @private 73024 */ 73025_initEvents : function() { 73026 this.createEvent("showEvent"); 73027 this.createEvent("keydownEvent"); 73028 this.createEvent("invalidDataEvent"); 73029 this.createEvent("revertEvent"); 73030 this.createEvent("saveEvent"); 73031 this.createEvent("cancelEvent"); 73032 this.createEvent("blurEvent"); 73033 this.createEvent("blockEvent"); 73034 this.createEvent("unblockEvent"); 73035}, 73036 73037/** 73038 * Initialize container element. 73039 * 73040 * @method _initContainerEl 73041 * @private 73042 */ 73043_initContainerEl : function() { 73044 if(this._elContainer) { 73045 YAHOO.util.Event.purgeElement(this._elContainer, true); 73046 this._elContainer.innerHTML = ""; 73047 } 73048 73049 var elContainer = document.createElement("div"); 73050 elContainer.id = this.getId() + "-container"; // Needed for tracking blur event 73051 elContainer.style.display = "none"; 73052 elContainer.tabIndex = 0; 73053 73054 this.className = lang.isArray(this.className) ? this.className : this.className ? [this.className] : []; 73055 this.className[this.className.length] = DT.CLASS_EDITOR; 73056 elContainer.className = this.className.join(" "); 73057 73058 document.body.insertBefore(elContainer, document.body.firstChild); 73059 this._elContainer = elContainer; 73060}, 73061 73062/** 73063 * Initialize container shim element. 73064 * 73065 * @method _initShimEl 73066 * @private 73067 */ 73068_initShimEl : function() { 73069 // Iframe shim 73070 if(this.useIFrame) { 73071 if(!this._elIFrame) { 73072 var elIFrame = document.createElement("iframe"); 73073 elIFrame.src = "javascript:false"; 73074 elIFrame.frameBorder = 0; 73075 elIFrame.scrolling = "no"; 73076 elIFrame.style.display = "none"; 73077 elIFrame.className = DT.CLASS_EDITOR_SHIM; 73078 elIFrame.tabIndex = -1; 73079 elIFrame.role = "presentation"; 73080 elIFrame.title = "Presentational iframe shim"; 73081 document.body.insertBefore(elIFrame, document.body.firstChild); 73082 this._elIFrame = elIFrame; 73083 } 73084 } 73085}, 73086 73087/** 73088 * Hides CellEditor UI at end of interaction. 73089 * 73090 * @method _hide 73091 */ 73092_hide : function() { 73093 this.getContainerEl().style.display = "none"; 73094 if(this._elIFrame) { 73095 this._elIFrame.style.display = "none"; 73096 } 73097 this.isActive = false;
73098 this.getDataTable()._oCellEditor = null; 73099}, 73100 73101 73102 73103 73104 73105 73106 73107 73108 73109 73110 73111///////////////////////////////////////////////////////////////////////////// 73112// 73113// Public properties 73114// 73115///////////////////////////////////////////////////////////////////////////// 73116/** 73117 * Implementer defined function that can submit the input value to a server. This 73118 * function must accept the arguments fnCallback and oNewValue. When the submission 73119 * is complete, the function must also call fnCallback(bSuccess, oNewValue) to 73120 * finish the save routine in the CellEditor. This function can also be used to 73121 * perform extra validation or input value manipulation. 73122 * 73123 * @property asyncSubmitter 73124 * @type HTMLFunction 73125 */ 73126asyncSubmitter : null, 73127 73128/** 73129 * Current value. 73130 * 73131 * @property value 73132 * @type MIXED 73133 */ 73134value : null, 73135 73136/** 73137 * Default value in case Record data is undefined. NB: Null values will not trigger 73138 * the default value. 73139 * 73140 * @property defaultValue 73141 * @type MIXED 73142 * @default null 73143 */ 73144defaultValue : null, 73145 73146/** 73147 * Validator function for input data, called from the DataTable instance scope, 73148 * receives the arguments (inputValue, currentValue, editorInstance) and returns 73149 * either the validated (or type-converted) value or undefined. 73150 * 73151 * @property validator 73152 * @type HTMLFunction 73153 * @default null 73154 */ 73155validator : null, 73156 73157/** 73158 * If validation is enabled, resets input field of invalid data. 73159 * 73160 * @property resetInvalidData 73161 * @type Boolean 73162 * @default true 73163 */ 73164resetInvalidData : true, 73165 73166/** 73167 * True if currently active. 73168 * 73169 * @property isActive 73170 * @type Boolean 73171 */ 73172isActive : false, 73173 73174/** 73175 * Text to display on Save button. 73176 * 73177 * @property LABEL_SAVE 73178 * @type HTML 73179 * @default "Save" 73180 */ 73181LABEL_SAVE : "Save", 73182 73183/** 73184 * Text to display on Cancel button. 73185 * 73186 * @property LABEL_CANCEL 73187 * @type HTML 73188 * @default "Cancel" 73189 */ 73190LABEL_CANCEL : "Cancel", 73191 73192/** 73193 * True if Save/Cancel buttons should not be displayed in the CellEditor. 73194 * 73195 * @property disableBtns 73196 * @type Boolean 73197 * @default false 73198 */ 73199disableBtns : false, 73200 73201/** 73202 * True if iframe shim for container element should be enabled. 73203 * 73204 * @property useIFrame 73205 * @type Boolean 73206 * @default false 73207 */ 73208useIFrame : false, 73209 73210/** 73211 * Custom CSS class or array of classes applied to the container element. 73212 * 73213 * @property className 73214 * @type String || String[] 73215 */ 73216className : null, 73217 73218 73219 73220 73221 73222///////////////////////////////////////////////////////////////////////////// 73223// 73224// Public methods 73225// 73226///////////////////////////////////////////////////////////////////////////// 73227/** 73228 * CellEditor instance name, for logging. 73229 * 73230 * @method toString 73231 * @return {String} Unique name of the CellEditor instance. 73232 */ 73233 73234toString : function() { 73235 return "CellEditor instance " + this._sId; 73236}, 73237 73238/** 73239 * CellEditor unique ID. 73240 * 73241 * @method getId 73242 * @return {String} Unique ID of the CellEditor instance. 73243 */ 73244 73245getId : function() { 73246 return this._sId; 73247}, 73248 73249/** 73250 * Returns reference to associated DataTable instance. 73251 * 73252 * @method getDataTable 73253 * @return {YAHOO.widget.DataTable} DataTable instance. 73254 */ 73255 73256getDataTable : function() { 73257 return this._oDataTable; 73258}, 73259 73260/** 73261 * Returns reference to associated Column instance. 73262 * 73263 * @method getColumn 73264 * @return {YAHOO.widget.Column} Column instance. 73265 */ 73266 73267getColumn : function() { 73268 return this._oColumn; 73269}, 73270 73271/** 73272 * Returns reference to associated Record instance. 73273 * 73274 * @method getRecord 73275 * @return {YAHOO.widget.Record} Record instance. 73276 */ 73277 73278getRecord : function() { 73279 return this._oRecord; 73280}, 73281 73282 73283 73284/** 73285 * Returns reference to associated TD element. 73286 * 73287 * @method getTdEl 73288 * @return {HTMLElement} TD element. 73289 */ 73290 73291getTdEl : function() { 73292 return this._elTd; 73293}, 73294 73295/** 73296 * Returns container element. 73297 * 73298 * @method getContainerEl 73299 * @return {HTMLElement} Reference to container element. 73300 */ 73301 73302getContainerEl : function() { 73303 return this._elContainer; 73304}, 73305 73306/** 73307 * Nulls out the entire CellEditor instance and related objects, removes attached 73308 * event listeners, and clears out DOM elements inside the container, removes 73309 * container from the DOM. 73310 * 73311 * @method destroy 73312 */ 73313destroy : function() { 73314 this.unsubscribeAll(); 73315 73316 // Column is late-binding in attach() 73317 var oColumn = this.getColumn(); 73318 if(oColumn) { 73319 oColumn.editor = null; 73320 } 73321 73322 var elContainer = this.getContainerEl(); 73323 if (elContainer) { 73324 Ev.purgeElement(elContainer, true); 73325 elContainer.parentNode.removeChild(elContainer); 73326 } 73327}, 73328 73329/** 73330 * Renders DOM elements and attaches event listeners. 73331 * 73332 * @method render 73333 */ 73334render : function() { 73335 if (!this._needsRender) { 73336 return; 73337 } 73338 73339 this._initContainerEl(); 73340 this._initShimEl(); 73341 73342 // Handle ESC key 73343 Ev.addListener(this.getContainerEl(), "keydown", function(e, oSelf) { 73344 // ESC cancels Cell Editor
73345 if((e.keyCode == 27)) { 73346 var target = Ev.getTarget(e); 73347 // workaround for Mac FF3 bug that disabled clicks when ESC hit when 73348 // select is open. [bug 2273056] 73349 if (target.nodeName && target.nodeName.toLowerCase() === 'select') { 73350 target.blur(); 73351 } 73352 oSelf.cancel(); 73353 } 73354 // Pass through event 73355 oSelf.fireEvent("keydownEvent", {editor:oSelf, event:e}); 73356 }, this); 73357 73358 this.renderForm(); 73359 73360 // Show Save/Cancel buttons 73361 if(!this.disableBtns) { 73362 this.renderBtns(); 73363 } 73364 73365 this.doAfterRender(); 73366 this._needsRender = false; 73367}, 73368 73369/** 73370 * Renders Save/Cancel buttons. 73371 * 73372 * @method renderBtns 73373 */ 73374renderBtns : function() { 73375 // Buttons 73376 var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div")); 73377 elBtnsDiv.className = DT.CLASS_BUTTON; 73378 73379 // Save button 73380 var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button")); 73381 elSaveBtn.className = DT.CLASS_DEFAULT; 73382 elSaveBtn.innerHTML = this.LABEL_SAVE; 73383 Ev.addListener(elSaveBtn, "click", function(oArgs) { 73384 this.save(); 73385 }, this, true); 73386 this._elSaveBtn = elSaveBtn; 73387 73388 // Cancel button 73389 var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button")); 73390 elCancelBtn.innerHTML = this.LABEL_CANCEL; 73391 Ev.addListener(elCancelBtn, "click", function(oArgs) { 73392 this.cancel(); 73393 }, this, true); 73394 this._elCancelBtn = elCancelBtn; 73395}, 73396 73397/** 73398 * Attach CellEditor for a new interaction. 73399 * 73400 * @method attach 73401 * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance. 73402 * @param elCell {HTMLElement} Cell to edit. 73403 */
73404attach : function(oDataTable, elCell) { 73405 // Validate 73406 if(oDataTable instanceof YAHOO.widget.DataTable) { 73407 this._oDataTable = oDataTable; 73408 73409 // Validate cell 73410 elCell = oDataTable.getTdEl(elCell); 73411 if(elCell) { 73412 this._elTd = elCell; 73413 73414 // Validate Column 73415 var oColumn = oDataTable.getColumn(elCell); 73416 if(oColumn) { 73417 this._oColumn = oColumn; 73418 73419 // Validate Record 73420 var oRecord = oDataTable.getRecord(elCell); 73421 if(oRecord) { 73422 this._oRecord = oRecord; 73423 var value = oRecord.getData(this.getColumn().getField()); 73424 this.value = (value !== undefined) ? value : this.defaultValue; 73425 return true; 73426 } 73427 } 73428 } 73429 } 73430 YAHOO.log("Could not attach CellEditor","error",this.toString()); 73431 return false; 73432}, 73433 73434/** 73435 * Moves container into position for display. 73436 * 73437 * @method move 73438 */ 73439move : function() { 73440 // Move Editor 73441 var elContainer = this.getContainerEl(), 73442 elTd = this.getTdEl(), 73443 x = Dom.getX(elTd), 73444 y = Dom.getY(elTd); 73445 73446 //TODO: remove scrolling logic 73447 // SF doesn't get xy for cells in scrolling table 73448 // when tbody display is set to block 73449 if(isNaN(x) || isNaN(y)) { 73450 var elTbody = this.getDataTable().getTbodyEl(); 73451 x = elTd.offsetLeft + // cell pos relative to table 73452 Dom.getX(elTbody.parentNode) - // plus table pos relative to document 73453 elTbody.scrollLeft; // minus tbody scroll 73454 y = elTd.offsetTop + // cell pos relative to table 73455 Dom.getY(elTbody.parentNode) - // plus table pos relative to document 73456 elTbody.scrollTop + // minus tbody scroll 73457 this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells 73458 } 73459 73460 elContainer.style.left = x + "px"; 73461 elContainer.style.top = y + "px"; 73462 73463 if(this._elIFrame) { 73464 this._elIFrame.style.left = x + "px"; 73465 this._elIFrame.style.top = y + "px"; 73466 } 73467}, 73468 73469/** 73470 * Displays CellEditor UI in the correct position. 73471 * 73472 * @method show 73473 */ 73474show : function() { 73475 var elContainer = this.getContainerEl(), 73476 elIFrame = this._elIFrame; 73477 this.resetForm(); 73478 this.isActive = true; 73479 elContainer.style.display = ""; 73480 if(elIFrame) { 73481 elIFrame.style.width = elContainer.offsetWidth + "px"; 73482 elIFrame.style.height = elContainer.offsetHeight + "px"; 73483 elIFrame.style.display = ""; 73484 } 73485 this.focus(); 73486 this.fireEvent("showEvent", {editor:this}); 73487 YAHOO.log("CellEditor shown", "info", this.toString()); 73488}, 73489 73490/** 73491 * Fires blockEvent 73492 * 73493 * @method block 73494 */ 73495block : function() { 73496 this.fireEvent("blockEvent", {editor:this}); 73497 YAHOO.log("CellEditor blocked", "info", this.toString()); 73498}, 73499 73500/** 73501 * Fires unblockEvent 73502 * 73503 * @method unblock 73504 */ 73505unblock : function() { 73506 this.fireEvent("unblockEvent", {editor:this}); 73507 YAHOO.log("CellEditor unblocked", "info", this.toString()); 73508}, 73509 73510/** 73511 * Saves value of CellEditor and hides UI. 73512 * 73513 * @method save 73514 */ 73515save : function() { 73516 // Get new value 73517 var inputValue = this.getInputValue(); 73518 var validValue = inputValue; 73519 73520 // Validate new value 73521 if(this.validator) { 73522 validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this); 73523 if(validValue === undefined ) { 73524 if(this.resetInvalidData) { 73525 this.resetForm(); 73526 } 73527 this.fireEvent("invalidDataEvent", 73528 {editor:this, oldData:this.value, newData:inputValue}); 73529 YAHOO.log("Could not save Cell Editor input due to invalid data " + 73530 lang.dump(inputValue), "warn", this.toString()); 73531 return; 73532 } 73533 } 73534 73535 var oSelf = this; 73536 var finishSave = function(bSuccess, oNewValue) { 73537 var oOrigValue = oSelf.value; 73538 if(bSuccess) { 73539 // Update new value 73540 oSelf.value = oNewValue; 73541 oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue); 73542 73543 // Hide CellEditor 73544 oSelf._hide(); 73545 73546 oSelf.fireEvent("saveEvent",
73547 {editor:oSelf, oldData:oOrigValue, newData:oSelf.value}); 73548 YAHOO.log("Cell Editor input saved", "info", this.toString()); 73549 } 73550 else { 73551 oSelf.resetForm(); 73552 oSelf.fireEvent("revertEvent", 73553 {editor:oSelf, oldData:oOrigValue, newData:oNewValue}); 73554 YAHOO.log("Could not save Cell Editor input " + 73555 lang.dump(oNewValue), "warn", oSelf.toString()); 73556 } 73557 oSelf.unblock(); 73558 }; 73559 73560 this.block(); 73561 if(lang.isFunction(this.asyncSubmitter)) { 73562 this.asyncSubmitter.call(this, finishSave, validValue); 73563 } 73564 else { 73565 finishSave(true, validValue); 73566 } 73567}, 73568 73569/** 73570 * Cancels CellEditor input and hides UI. 73571 * 73572 * @method cancel 73573 */ 73574cancel : function() { 73575 if(this.isActive) { 73576 this._hide(); 73577 this.fireEvent("cancelEvent", {editor:this}); 73578 YAHOO.log("CellEditor canceled", "info", this.toString()); 73579 } 73580 else { 73581 YAHOO.log("Unable to cancel CellEditor", "warn", this.toString()); 73582 } 73583}, 73584 73585/** 73586 * Renders form elements. 73587 * 73588 * @method renderForm 73589 */ 73590renderForm : function() { 73591 // To be implemented by subclass 73592}, 73593 73594/** 73595 * Access to add additional event listeners. 73596 * 73597 * @method doAfterRender 73598 */ 73599doAfterRender : function() { 73600 // To be implemented by subclass 73601}, 73602 73603 73604/** 73605 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 73606 * to save input without them. 73607 * 73608 * @method handleDisabledBtns 73609 */ 73610handleDisabledBtns : function() { 73611 // To be implemented by subclass 73612}, 73613 73614/** 73615 * Resets CellEditor UI to initial state. 73616 * 73617 * @method resetForm 73618 */ 73619resetForm : function() { 73620 // To be implemented by subclass 73621}, 73622 73623/** 73624 * Sets focus in CellEditor. 73625 * 73626 * @method focus 73627 */ 73628focus : function() { 73629 // To be implemented by subclass 73630}, 73631 73632/** 73633 * Retrieves input value from CellEditor. 73634 * 73635 * @method getInputValue 73636 */ 73637getInputValue : function() { 73638 // To be implemented by subclass 73639} 73640 73641}; 73642 73643lang.augmentProto(BCE, util.EventProvider); 73644 73645
73646///////////////////////////////////////////////////////////////////////////// 73647// 73648// Custom Events 73649// 73650///////////////////////////////////////////////////////////////////////////// 73651 73652/** 73653 * Fired when a CellEditor is shown. 73654 * 73655 * @event showEvent 73656 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73657 */ 73658 73659/** 73660 * Fired when a CellEditor has a keydown. 73661 * 73662 * @event keydownEvent 73663 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73664 * @param oArgs.event {HTMLEvent} The event object. 73665 */ 73666 73667/** 73668 * Fired when a CellEditor input is reverted due to invalid data. 73669 * 73670 * @event invalidDataEvent 73671 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73672 * @param oArgs.newData {Object} New data value from form input field. 73673 * @param oArgs.oldData {Object} Old data value. 73674 */ 73675 73676/** 73677 * Fired when a CellEditor input is reverted due to asyncSubmitter failure. 73678 * 73679 * @event revertEvent 73680 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73681 * @param oArgs.newData {Object} New data value from form input field. 73682 * @param oArgs.oldData {Object} Old data value. 73683 */ 73684 73685/** 73686 * Fired when a CellEditor input is saved. 73687 * 73688 * @event saveEvent 73689 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73690 * @param oArgs.newData {Object} New data value from form input field. 73691 * @param oArgs.oldData {Object} Old data value. 73692 */ 73693 73694/** 73695 * Fired when a CellEditor input is canceled. 73696 * 73697 * @event cancelEvent 73698 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73699 */ 73700 73701/** 73702 * Fired when a CellEditor has a blur event. 73703 * 73704 * @event blurEvent 73705 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 73706 */ 73707 73708 73709 73710 73711 73712 73713 73714 73715 73716 73717 73718 73719 73720 73721/****************************************************************************/ 73722/****************************************************************************/ 73723/****************************************************************************/ 73724 73725/** 73726 * The CheckboxCellEditor class provides functionality for inline editing 73727 * DataTable cell data with checkboxes. 73728 * 73729 * @namespace YAHOO.widget 73730 * @class CheckboxCellEditor 73731 * @extends YAHOO.widget.BaseCellEditor 73732 * @constructor 73733 * @param oConfigs {Object} (Optional) Object literal of configs. 73734 */ 73735widget.CheckboxCellEditor = function(oConfigs) { 73736 oConfigs = oConfigs || {}; 73737 this._sId = this._sId || Dom.generateId(null, "yui-checkboxceditor"); // "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++; 73738 YAHOO.widget.BaseCellEditor._nCount++; 73739 widget.CheckboxCellEditor.superclass.constructor.call(this, oConfigs.type || "checkbox", oConfigs); 73740}; 73741 73742// CheckboxCellEditor extends BaseCellEditor 73743lang.extend(widget.CheckboxCellEditor, BCE, { 73744 73745///////////////////////////////////////////////////////////////////////////// 73746// 73747// CheckboxCellEditor public properties 73748// 73749///////////////////////////////////////////////////////////////////////////// 73750/** 73751 * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"]) 73752 * or a an array of objects (e.g., [{label:"red", value:"#FF0000"}, 73753 * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]). String 73754 * values are treated as markup and inserted into the DOM as innerHTML. 73755 * 73756 * @property checkboxOptions 73757 * @type HTML[] | Object[] 73758 */ 73759checkboxOptions : null, 73760 73761/** 73762 * Reference to the checkbox elements. 73763 * 73764 * @property checkboxes 73765 * @type HTMLElement[] 73766 */ 73767checkboxes : null, 73768 73769/** 73770 * Array of checked values 73771 * 73772 * @property value 73773 * @type String[] 73774 */ 73775value : null, 73776 73777///////////////////////////////////////////////////////////////////////////// 73778// 73779// CheckboxCellEditor public methods 73780// 73781///////////////////////////////////////////////////////////////////////////// 73782 73783/** 73784 * Render a form with input(s) type=checkbox. 73785 * 73786 * @method renderForm 73787 */ 73788renderForm : function() { 73789 if(lang.isArray(this.checkboxOptions)) { 73790 var checkboxOption, checkboxValue, checkboxId, elLabel, j, len; 73791 73792 // Create the checkbox buttons in an IE-friendly way... 73793 for(j=0,len=this.checkboxOptions.length; j<len; j++) { 73794 checkboxOption = this.checkboxOptions[j]; 73795 checkboxValue = lang.isValue(checkboxOption.value) ? 73796 checkboxOption.value : checkboxOption; 73797 73798 checkboxId = this.getId() + "-chk" + j; 73799 this.getContainerEl().innerHTML += "<input type=\"checkbox\"" + 73800 " id=\"" + checkboxId + "\"" + // Needed for label 73801 " value=\"" + checkboxValue + "\" />"; 73802 73803 // Create the labels in an IE-friendly way 73804 elLabel = this.getContainerEl().appendChild(document.createElement("label")); 73805 elLabel.htmlFor = checkboxId; 73806 elLabel.innerHTML = lang.isValue(checkboxOption.label) ? 73807 checkboxOption.label : checkboxOption; 73808 } 73809 73810 // Store the reference to the checkbox elements 73811 var allCheckboxes = []; 73812 for(j=0; j<len; j++) { 73813 allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2]; 73814 } 73815 this.checkboxes = allCheckboxes; 73816 73817 if(this.disableBtns) { 73818 this.handleDisabledBtns(); 73819 } 73820 } 73821 else { 73822 YAHOO.log("Could not find checkboxOptions", "error", this.toString()); 73823 } 73824}, 73825 73826/**
73827 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 73828 * to save input without them. 73829 * 73830 * @method handleDisabledBtns 73831 */ 73832handleDisabledBtns : function() { 73833 Ev.addListener(this.getContainerEl(), "click", function(v){ 73834 if(Ev.getTarget(v).tagName.toLowerCase() === "input") { 73835 // Save on blur 73836 this.save(); 73837 } 73838 }, this, true); 73839}, 73840 73841/** 73842 * Resets CheckboxCellEditor UI to initial state. 73843 * 73844 * @method resetForm 73845 */ 73846resetForm : function() { 73847 // Normalize to array 73848 var originalValues = lang.isArray(this.value) ? this.value : [this.value]; 73849 73850 // Match checks to value 73851 for(var i=0, j=this.checkboxes.length; i<j; i++) { 73852 this.checkboxes[i].checked = false; 73853 for(var k=0, len=originalValues.length; k<len; k++) { 73854 if(this.checkboxes[i].value == originalValues[k]) { 73855 this.checkboxes[i].checked = true; 73856 } 73857 } 73858 } 73859}, 73860 73861/** 73862 * Sets focus in CheckboxCellEditor. 73863 * 73864 * @method focus 73865 */ 73866focus : function() { 73867 this.checkboxes[0].focus(); 73868}, 73869 73870/** 73871 * Retrieves input value from CheckboxCellEditor. 73872 * 73873 * @method getInputValue 73874 */ 73875getInputValue : function() { 73876 var checkedValues = []; 73877 for(var i=0, j=this.checkboxes.length; i<j; i++) { 73878 if(this.checkboxes[i].checked) { 73879 checkedValues[checkedValues.length] = this.checkboxes[i].value; 73880 } 73881 } 73882 return checkedValues; 73883} 73884 73885}); 73886 73887// Copy static members to CheckboxCellEditor class 73888lang.augmentObject(widget.CheckboxCellEditor, BCE); 73889 73890 73891 73892 73893 73894 73895 73896 73897/****************************************************************************/ 73898/****************************************************************************/ 73899/****************************************************************************/ 73900 73901/** 73902 * The DataCellEditor class provides functionality for inline editing 73903 * DataTable cell data with a YUI Calendar. 73904 * 73905 * @namespace YAHOO.widget 73906 * @class DateCellEditor 73907 * @extends YAHOO.widget.BaseCellEditor 73908 * @constructor 73909 * @param oConfigs {Object} (Optional) Object literal of configs. 73910 */ 73911widget.DateCellEditor = function(oConfigs) { 73912 oConfigs = oConfigs || {}; 73913 this._sId = this._sId || Dom.generateId(null, "yui-dateceditor"); // "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++; 73914 YAHOO.widget.BaseCellEditor._nCount++; 73915 widget.DateCellEditor.superclass.constructor.call(this, oConfigs.type || "date", oConfigs); 73916}; 73917 73918// CheckboxCellEditor extends BaseCellEditor 73919lang.extend(widget.DateCellEditor, BCE, { 73920 73921///////////////////////////////////////////////////////////////////////////// 73922// 73923// DateCellEditor public properties 73924// 73925///////////////////////////////////////////////////////////////////////////// 73926/** 73927 * Reference to Calendar instance. 73928 * 73929 * @property calendar 73930 * @type YAHOO.widget.Calendar 73931 */ 73932calendar : null, 73933 73934/** 73935 * Configs for the calendar instance, to be passed to Calendar constructor. 73936 * 73937 * @property calendarOptions 73938 * @type Object 73939 */ 73940calendarOptions : null, 73941 73942/** 73943 * Default value. 73944 * 73945 * @property defaultValue 73946 * @type Date 73947 * @default new Date() 73948 */ 73949defaultValue : new Date(), 73950 73951 73952///////////////////////////////////////////////////////////////////////////// 73953// 73954// DateCellEditor public methods 73955// 73956///////////////////////////////////////////////////////////////////////////// 73957 73958/** 73959 * Render a Calendar. 73960 * 73961 * @method renderForm 73962 */ 73963renderForm : function() { 73964 // Calendar widget 73965 if(YAHOO.widget.Calendar) { 73966 var calContainer = this.getContainerEl().appendChild(document.createElement("div")); 73967 calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor 73968 var calendar = 73969 new YAHOO.widget.Calendar(this.getId() + "-date", 73970 calContainer.id, this.calendarOptions); 73971 calendar.render(); 73972 calContainer.style.cssFloat = "none"; 73973 73974 // Bug 2528576 73975 calendar.hideEvent.subscribe(function() {this.cancel();}, this, true); 73976 73977 if(ua.ie) { 73978 var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div")); 73979 calFloatClearer.style.clear = "both"; 73980 } 73981 73982 this.calendar = calendar; 73983 73984 if(this.disableBtns) { 73985 this.handleDisabledBtns(); 73986 } 73987 } 73988 else { 73989 YAHOO.log("Could not find YUI Calendar", "error", this.toString()); 73990 } 73991 73992}, 73993 73994/**
73995 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 73996 * to save input without them. 73997 * 73998 * @method handleDisabledBtns 73999 */ 74000handleDisabledBtns : function() { 74001 this.calendar.selectEvent.subscribe(function(v){ 74002 // Save on select 74003 this.save(); 74004 }, this, true); 74005}, 74006 74007/** 74008 * Resets DateCellEditor UI to initial state. 74009 * 74010 * @method resetForm 74011 */ 74012resetForm : function() { 74013 var value = this.value || (new Date()); 74014 this.calendar.select(value); 74015 this.calendar.cfg.setProperty("pagedate",value,false); 74016 this.calendar.render(); 74017 // Bug 2528576 74018 this.calendar.show(); 74019}, 74020 74021/** 74022 * Sets focus in DateCellEditor. 74023 * 74024 * @method focus 74025 */ 74026focus : function() { 74027 // To be impmlemented by subclass 74028}, 74029 74030/** 74031 * Retrieves input value from DateCellEditor. 74032 * 74033 * @method getInputValue 74034 */ 74035getInputValue : function() { 74036 return this.calendar.getSelectedDates()[0]; 74037} 74038 74039}); 74040 74041// Copy static members to DateCellEditor class 74042lang.augmentObject(widget.DateCellEditor, BCE); 74043 74044 74045 74046 74047 74048 74049 74050 74051 74052/****************************************************************************/ 74053/****************************************************************************/ 74054/****************************************************************************/ 74055 74056/** 74057 * The DropdownCellEditor class provides functionality for inline editing 74058 * DataTable cell data a SELECT element. 74059 * 74060 * @namespace YAHOO.widget 74061 * @class DropdownCellEditor 74062 * @extends YAHOO.widget.BaseCellEditor 74063 * @constructor 74064 * @param oConfigs {Object} (Optional) Object literal of configs. 74065 */ 74066widget.DropdownCellEditor = function(oConfigs) { 74067 oConfigs = oConfigs || {}; 74068 this._sId = this._sId || Dom.generateId(null, "yui-dropdownceditor"); // "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++; 74069 YAHOO.widget.BaseCellEditor._nCount++; 74070 widget.DropdownCellEditor.superclass.constructor.call(this, oConfigs.type || "dropdown", oConfigs); 74071}; 74072 74073// DropdownCellEditor extends BaseCellEditor 74074lang.extend(widget.DropdownCellEditor, BCE, { 74075 74076///////////////////////////////////////////////////////////////////////////// 74077// 74078// DropdownCellEditor public properties 74079// 74080///////////////////////////////////////////////////////////////////////////// 74081/** 74082 * Array of dropdown values. Can either be a simple array (e.g., 74083 * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g., 74084 * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"}, 74085 * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]). String 74086 * values are treated as markup and inserted into the DOM as innerHTML. 74087 * 74088 * @property dropdownOptions 74089 * @type HTML[] | Object[] 74090 */ 74091dropdownOptions : null, 74092 74093/** 74094 * Reference to Dropdown element. 74095 * 74096 * @property dropdown 74097 * @type HTMLElement 74098 */ 74099dropdown : null, 74100 74101/** 74102 * Enables multi-select. 74103 * 74104 * @property multiple 74105 * @type Boolean 74106 */ 74107multiple : false, 74108 74109/** 74110 * Specifies number of visible options. 74111 * 74112 * @property size 74113 * @type Number 74114 */ 74115size : null, 74116 74117///////////////////////////////////////////////////////////////////////////// 74118// 74119// DropdownCellEditor public methods 74120// 74121///////////////////////////////////////////////////////////////////////////// 74122 74123/** 74124 * Render a form with select element. 74125 * 74126 * @method renderForm 74127 */ 74128renderForm : function() { 74129 var elDropdown = this.getContainerEl().appendChild(document.createElement("select")); 74130 elDropdown.style.zoom = 1; 74131 if(this.multiple) { 74132 elDropdown.multiple = "multiple"; 74133 } 74134 if(lang.isNumber(this.size)) { 74135 elDropdown.size = this.size; 74136 } 74137 this.dropdown = elDropdown; 74138 74139 if(lang.isArray(this.dropdownOptions)) { 74140 var dropdownOption, elOption; 74141 for(var i=0, j=this.dropdownOptions.length; i<j; i++) { 74142 dropdownOption = this.dropdownOptions[i]; 74143 elOption = document.createElement("option"); 74144 elOption.value = (lang.isValue(dropdownOption.value)) ? 74145 dropdownOption.value : dropdownOption; 74146 elOption.innerHTML = (lang.isValue(dropdownOption.label)) ? 74147 dropdownOption.label : dropdownOption; 74148 elOption = elDropdown.appendChild(elOption); 74149 } 74150 74151 if(this.disableBtns) { 74152 this.handleDisabledBtns(); 74153 } 74154 } 74155}, 74156 74157/**
74158 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 74159 * to save input without them. 74160 * 74161 * @method handleDisabledBtns 74162 */ 74163handleDisabledBtns : function() { 74164 // Save on blur for multi-select 74165 if(this.multiple) { 74166 Ev.addListener(this.dropdown, "blur", function(v){ 74167 // Save on change 74168 this.save(); 74169 }, this, true); 74170 } 74171 // Save on change for single-select 74172 else { 74173 if(!ua.ie) { 74174 Ev.addListener(this.dropdown, "change", function(v){ 74175 // Save on change 74176 this.save(); 74177 }, this, true); 74178 } 74179 else { 74180 // Bug 2529274: "change" event is not keyboard accessible in IE6 74181 Ev.addListener(this.dropdown, "blur", function(v){ 74182 this.save(); 74183 }, this, true); 74184 Ev.addListener(this.dropdown, "click", function(v){ 74185 this.save(); 74186 }, this, true); 74187 } 74188 } 74189}, 74190 74191/** 74192 * Resets DropdownCellEditor UI to initial state. 74193 * 74194 * @method resetForm 74195 */ 74196resetForm : function() { 74197 var allOptions = this.dropdown.options, 74198 i=0, j=allOptions.length; 74199 74200 // Look for multi-select selections 74201 if(lang.isArray(this.value)) { 74202 var allValues = this.value, 74203 m=0, n=allValues.length, 74204 hash = {}; 74205 // Reset all selections and stash options in a value hash 74206 for(; i<j; i++) { 74207 allOptions[i].selected = false; 74208 hash[allOptions[i].value] = allOptions[i]; 74209 } 74210 for(; m<n; m++) { 74211 if(hash[allValues[m]]) { 74212 hash[allValues[m]].selected = true; 74213 } 74214 } 74215 } 74216 // Only need to look for a single selection 74217 else { 74218 for(; i<j; i++) { 74219 if(this.value == allOptions[i].value) { 74220 allOptions[i].selected = true; 74221 } 74222 } 74223 } 74224}, 74225 74226/** 74227 * Sets focus in DropdownCellEditor. 74228 * 74229 * @method focus 74230 */ 74231focus : function() { 74232 this.getDataTable()._focusEl(this.dropdown); 74233}, 74234 74235/** 74236 * Retrieves input value from DropdownCellEditor. 74237 * 74238 * @method getInputValue 74239 */ 74240getInputValue : function() { 74241 var allOptions = this.dropdown.options; 74242 74243 // Look for multiple selections 74244 if(this.multiple) { 74245 var values = [], 74246 i=0, j=allOptions.length; 74247 for(; i<j; i++) { 74248 if(allOptions[i].selected) { 74249 values.push(allOptions[i].value); 74250 } 74251 } 74252 return values; 74253 } 74254 // Only need to look for single selection 74255 else { 74256 return allOptions[allOptions.selectedIndex].value; 74257 } 74258} 74259 74260}); 74261 74262// Copy static members to DropdownCellEditor class 74263lang.augmentObject(widget.DropdownCellEditor, BCE); 74264 74265 74266 74267 74268 74269 74270/****************************************************************************/ 74271/****************************************************************************/ 74272/****************************************************************************/ 74273 74274/** 74275 * The RadioCellEditor class provides functionality for inline editing 74276 * DataTable cell data with radio buttons. 74277 * 74278 * @namespace YAHOO.widget 74279 * @class RadioCellEditor 74280 * @extends YAHOO.widget.BaseCellEditor 74281 * @constructor 74282 * @param oConfigs {Object} (Optional) Object literal of configs. 74283 */ 74284widget.RadioCellEditor = function(oConfigs) { 74285 oConfigs = oConfigs || {}; 74286 this._sId = this._sId || Dom.generateId(null, "yui-radioceditor"); // "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++; 74287 YAHOO.widget.BaseCellEditor._nCount++; 74288 widget.RadioCellEditor.superclass.constructor.call(this, oConfigs.type || "radio", oConfigs); 74289}; 74290 74291// RadioCellEditor extends BaseCellEditor 74292lang.extend(widget.RadioCellEditor, BCE, { 74293 74294///////////////////////////////////////////////////////////////////////////// 74295// 74296// RadioCellEditor public properties 74297// 74298///////////////////////////////////////////////////////////////////////////// 74299/** 74300 * Reference to radio elements. 74301 * 74302 * @property radios 74303 * @type HTMLElement[] 74304 */ 74305radios : null, 74306 74307/** 74308 * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"]) 74309 * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1}, 74310 * {label:"maybe", value:0}]). String values are treated as markup and inserted 74311 * into the DOM as innerHTML. 74312 * 74313 * @property radioOptions 74314 * @type HTML[] | Object[] 74315 */ 74316radioOptions : null, 74317 74318///////////////////////////////////////////////////////////////////////////// 74319// 74320// RadioCellEditor public methods 74321// 74322///////////////////////////////////////////////////////////////////////////// 74323 74324/** 74325 * Render a form with input(s) type=radio. 74326 * 74327 * @method renderForm 74328 */ 74329renderForm : function() { 74330 if(lang.isArray(this.radioOptions)) { 74331 var radioOption, radioValue, radioId, elLabel; 74332 74333 // Create the radio buttons in an IE-friendly way 74334 for(var i=0, len=this.radioOptions.length; i<len; i++) { 74335 radioOption = this.radioOptions[i]; 74336 radioValue = lang.isValue(radioOption.value) ? 74337 radioOption.value : radioOption; 74338 radioId = this.getId() + "-radio" + i; 74339 this.getContainerEl().innerHTML += "<input type=\"radio\"" + 74340 " name=\"" + this.getId() + "\"" + 74341 " value=\"" + radioValue + "\"" + 74342 " id=\"" + radioId + "\" />"; // Needed for label 74343 74344 // Create the labels in an IE-friendly way 74345 elLabel = this.getContainerEl().appendChild(document.createElement("label")); 74346 elLabel.htmlFor = radioId; 74347 elLabel.innerHTML = (lang.isValue(radioOption.label)) ? 74348 radioOption.label : radioOption; 74349 } 74350 74351 // Store the reference to the checkbox elements 74352 var allRadios = [], 74353 elRadio; 74354 for(var j=0; j<len; j++) { 74355 elRadio = this.getContainerEl().childNodes[j*2]; 74356 allRadios[allRadios.length] = elRadio; 74357 } 74358 this.radios = allRadios; 74359 74360 if(this.disableBtns) { 74361 this.handleDisabledBtns(); 74362 } 74363 } 74364 else { 74365 YAHOO.log("Could not find radioOptions", "error", this.toString()); 74366 } 74367}, 74368 74369/**
74370 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 74371 * to save input without them. 74372 * 74373 * @method handleDisabledBtns 74374 */ 74375handleDisabledBtns : function() { 74376 Ev.addListener(this.getContainerEl(), "click", function(v){ 74377 if(Ev.getTarget(v).tagName.toLowerCase() === "input") { 74378 // Save on blur 74379 this.save(); 74380 } 74381 }, this, true); 74382}, 74383 74384/** 74385 * Resets RadioCellEditor UI to initial state. 74386 * 74387 * @method resetForm 74388 */ 74389resetForm : function() { 74390 for(var i=0, j=this.radios.length; i<j; i++) { 74391 var elRadio = this.radios[i]; 74392 if(this.value == elRadio.value) { 74393 elRadio.checked = true; 74394 return; 74395 } 74396 } 74397}, 74398 74399/** 74400 * Sets focus in RadioCellEditor. 74401 * 74402 * @method focus 74403 */ 74404focus : function() { 74405 for(var i=0, j=this.radios.length; i<j; i++) { 74406 if(this.radios[i].checked) { 74407 this.radios[i].focus(); 74408 return; 74409 } 74410 } 74411}, 74412 74413/** 74414 * Retrieves input value from RadioCellEditor. 74415 * 74416 * @method getInputValue 74417 */ 74418getInputValue : function() { 74419 for(var i=0, j=this.radios.length; i<j; i++) { 74420 if(this.radios[i].checked) { 74421 return this.radios[i].value; 74422 } 74423 } 74424} 74425 74426}); 74427 74428// Copy static members to RadioCellEditor class 74429lang.augmentObject(widget.RadioCellEditor, BCE); 74430 74431 74432 74433 74434 74435 74436/****************************************************************************/ 74437/****************************************************************************/ 74438/****************************************************************************/ 74439 74440/** 74441 * The TextareaCellEditor class provides functionality for inline editing 74442 * DataTable cell data with a TEXTAREA element. 74443 * 74444 * @namespace YAHOO.widget 74445 * @class TextareaCellEditor 74446 * @extends YAHOO.widget.BaseCellEditor 74447 * @constructor 74448 * @param oConfigs {Object} (Optional) Object literal of configs. 74449 */ 74450widget.TextareaCellEditor = function(oConfigs) { 74451 oConfigs = oConfigs || {}; 74452 this._sId = this._sId || Dom.generateId(null, "yui-textareaceditor");// "yui-textareaceditor" + ; 74453 YAHOO.widget.BaseCellEditor._nCount++; 74454 widget.TextareaCellEditor.superclass.constructor.call(this, oConfigs.type || "textarea", oConfigs); 74455}; 74456 74457// TextareaCellEditor extends BaseCellEditor 74458lang.extend(widget.TextareaCellEditor, BCE, { 74459 74460///////////////////////////////////////////////////////////////////////////// 74461// 74462// TextareaCellEditor public properties 74463// 74464///////////////////////////////////////////////////////////////////////////// 74465/** 74466 * Reference to textarea element. 74467 * 74468 * @property textarea 74469 * @type HTMLElement 74470 */ 74471textarea : null, 74472 74473 74474///////////////////////////////////////////////////////////////////////////// 74475// 74476// TextareaCellEditor public methods 74477// 74478///////////////////////////////////////////////////////////////////////////// 74479 74480/** 74481 * Render a form with textarea. 74482 * 74483 * @method renderForm 74484 */ 74485renderForm : function() { 74486 var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea")); 74487 this.textarea = elTextarea; 74488 74489 if(this.disableBtns) { 74490 this.handleDisabledBtns(); 74491 } 74492}, 74493 74494/** 74495 * After rendering form, if disabledBtns is set to true, then sets up a mechanism 74496 * to save input without them. 74497 * 74498 * @method handleDisabledBtns 74499 */ 74500handleDisabledBtns : function() { 74501 Ev.addListener(this.textarea, "blur", function(v){ 74502 // Save on blur 74503 this.save(); 74504 }, this, true); 74505}, 74506 74507/** 74508 * Moves TextareaCellEditor UI to a cell. 74509 * 74510 * @method move 74511 */ 74512move : function() { 74513 this.textarea.style.width = this.getTdEl().offsetWidth + "px"; 74514 this.textarea.style.height = "3em"; 74515 YAHOO.widget.TextareaCellEditor.superclass.move.call(this); 74516}, 74517 74518/** 74519 * Resets TextareaCellEditor UI to initial state. 74520 * 74521 * @method resetForm 74522 */ 74523resetForm : function() { 74524 this.textarea.value = this.value; 74525}, 74526 74527/** 74528 * Sets focus in TextareaCellEditor. 74529 * 74530 * @method focus 74531 */ 74532focus : function() { 74533 // Bug 2303181, Bug 2263600 74534 this.getDataTable()._focusEl(this.textarea); 74535 this.textarea.select(); 74536}, 74537 74538/** 74539 * Retrieves input value from TextareaCellEditor. 74540 * 74541 * @method getInputValue 74542 */ 74543getInputValue : function() { 74544 return this.textarea.value; 74545} 74546 74547}); 74548 74549// Copy static members to TextareaCellEditor class 74550lang.augmentObject(widget.TextareaCellEditor, BCE); 74551 74552 74553 74554 74555 74556 74557 74558 74559 74560/****************************************************************************/ 74561/****************************************************************************/ 74562/****************************************************************************/ 74563 74564/** 74565 * The TextboxCellEditor class provides functionality for inline editing 74566 * DataTable cell data with an INPUT TYPE=TEXT element. 74567 * 74568 * @namespace YAHOO.widget 74569 * @class TextboxCellEditor 74570 * @extends YAHOO.widget.BaseCellEditor 74571 * @constructor 74572 * @param oConfigs {Object} (Optional) Object literal of configs. 74573 */ 74574widget.TextboxCellEditor = function(oConfigs) { 74575 oConfigs = oConfigs || {}; 74576 this._sId = this._sId || Dom.generateId(null, "yui-textboxceditor");// "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++; 74577 YAHOO.widget.BaseCellEditor._nCount++; 74578 widget.TextboxCellEditor.superclass.constructor.call(this, oConfigs.type || "textbox", oConfigs); 74579}; 74580 74581// TextboxCellEditor extends BaseCellEditor 74582lang.extend(widget.TextboxCellEditor, BCE, { 74583 74584///////////////////////////////////////////////////////////////////////////// 74585// 74586// TextboxCellEditor public properties 74587// 74588///////////////////////////////////////////////////////////////////////////// 74589/** 74590 * Reference to the textbox element. 74591 * 74592 * @property textbox 74593 */ 74594textbox : null, 74595 74596///////////////////////////////////////////////////////////////////////////// 74597// 74598// TextboxCellEditor public methods 74599// 74600///////////////////////////////////////////////////////////////////////////// 74601 74602/** 74603 * Render a form with input type=text. 74604 * 74605 * @method renderForm 74606 */ 74607renderForm : function() { 74608 var elTextbox; 74609 // Bug 1802582: SF3/Mac needs a form element wrapping the input 74610 if(ua.webkit>420) { 74611 elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input")); 74612 } 74613 else { 74614 elTextbox = this.getContainerEl().appendChild(document.createElement("input")); 74615 } 74616 elTextbox.type = "text"; 74617 this.textbox = elTextbox; 74618 74619 // Save on enter by default 74620 // Bug: 1802582 Set up a listener on each textbox to track on keypress 74621 // since SF/OP can't preventDefault on keydown 74622 Ev.addListener(elTextbox, "keypress", function(v){ 74623 if((v.keyCode === 13)) { 74624 // Prevent form submit 74625 YAHOO.util.Event.preventDefault(v); 74626 this.save(); 74627 } 74628 }, this, true); 74629 74630 if(this.disableBtns) { 74631 // By default this is no-op since enter saves by default 74632 this.handleDisabledBtns(); 74633 } 74634}, 74635 74636/** 74637 * Moves TextboxCellEditor UI to a cell. 74638 * 74639 * @method move 74640 */ 74641move : function() { 74642 this.textbox.style.width = this.getTdEl().offsetWidth + "px"; 74643 widget.TextboxCellEditor.superclass.move.call(this); 74644}, 74645 74646/** 74647 * Resets TextboxCellEditor UI to initial state. 74648 * 74649 * @method resetForm 74650 */ 74651resetForm : function() { 74652 this.textbox.value = lang.isValue(this.value) ? this.value.toString() : ""; 74653}, 74654 74655/** 74656 * Sets focus in TextboxCellEditor. 74657 *
74658 * @method focus 74659 */ 74660focus : function() { 74661 // Bug 2303181, Bug 2263600 74662 this.getDataTable()._focusEl(this.textbox); 74663 this.textbox.select(); 74664}, 74665 74666/** 74667 * Returns new value for TextboxCellEditor. 74668 * 74669 * @method getInputValue 74670 */ 74671getInputValue : function() { 74672 return this.textbox.value; 74673} 74674 74675}); 74676 74677// Copy static members to TextboxCellEditor class 74678lang.augmentObject(widget.TextboxCellEditor, BCE); 74679 74680 74681 74682 74683 74684 74685 74686///////////////////////////////////////////////////////////////////////////// 74687// 74688// DataTable extension 74689// 74690///////////////////////////////////////////////////////////////////////////// 74691 74692/** 74693 * CellEditor subclasses. 74694 * @property DataTable.Editors 74695 * @type Object 74696 * @static 74697 */ 74698DT.Editors = { 74699 checkbox : widget.CheckboxCellEditor, 74700 "date" : widget.DateCellEditor, 74701 dropdown : widget.DropdownCellEditor, 74702 radio : widget.RadioCellEditor, 74703 textarea : widget.TextareaCellEditor, 74704 textbox : widget.TextboxCellEditor 74705}; 74706 74707/****************************************************************************/ 74708/****************************************************************************/ 74709/****************************************************************************/ 74710 74711/** 74712 * Factory class for instantiating a BaseCellEditor subclass. 74713 * 74714 * @namespace YAHOO.widget 74715 * @class CellEditor 74716 * @extends YAHOO.widget.BaseCellEditor 74717 * @constructor 74718 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors. 74719 * @param oConfigs {Object} (Optional) Object literal of configs. 74720 */ 74721widget.CellEditor = function(sType, oConfigs) { 74722 // Point to one of the subclasses 74723 if(sType && DT.Editors[sType]) { 74724 lang.augmentObject(BCE, DT.Editors[sType]); 74725 return new DT.Editors[sType](oConfigs); 74726 } 74727 else { 74728 return new BCE(null, oConfigs); 74729 } 74730}; 74731 74732var CE = widget.CellEditor; 74733 74734// Copy static members to CellEditor class 74735lang.augmentObject(CE, BCE); 74736 74737 74738})(); 74739 74740YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.9.0", build: "2800"}); 74741/* 74742Copyright (c) 2011, Yahoo! Inc. All rights reserved. 74743Code licensed under the BSD License: 74744http://developer.yahoo.com/yui/license.html 74745version: 2.9.0 74746*/ 74747(function () { 74748 var Dom = YAHOO.util.Dom, 74749 Event = YAHOO.util.Event, 74750 Lang = YAHOO.lang, 74751 Widget = YAHOO.widget; 74752 74753 74754 74755/** 74756 * The treeview widget is a generic tree building tool. 74757 * @module treeview 74758 * @title TreeView Widget 74759 * @requires yahoo, dom, event 74760 * @optional animation, json, calendar 74761 * @namespace YAHOO.widget 74762 */ 74763 74764/** 74765 * Contains the tree view state data and the root node. 74766 * 74767 * @class TreeView 74768 * @uses YAHOO.util.EventProvider 74769 * @constructor 74770 * @param {string|HTMLElement} id The id of the element, or the element itself that the tree will be inserted into. 74771 * Existing markup in this element, if valid, will be used to build the tree 74772 * @param {Array|Object|String} oConfig (optional) If present, it will be used to build the tree via method <a href="#method_buildTreeFromObject">buildTreeFromObject</a> 74773 * 74774 */ 74775YAHOO.widget.TreeView = function(id, oConfig) { 74776 if (id) { this.init(id); } 74777 if (oConfig) { 74778 this.buildTreeFromObject(oConfig); 74779 } else if (Lang.trim(this._el.innerHTML)) { 74780 this.buildTreeFromMarkup(id); 74781 } 74782}; 74783 74784var TV = Widget.TreeView; 74785 74786TV.prototype = { 74787 74788 /** 74789 * The id of tree container element 74790 * @property id
74791 * @type String 74792 */ 74793 id: null, 74794 74795 /** 74796 * The host element for this tree 74797 * @property _el 74798 * @private 74799 * @type HTMLelement 74800 */ 74801 _el: null, 74802 74803 /** 74804 * Flat collection of all nodes in this tree. This is a sparse 74805 * array, so the length property can't be relied upon for a 74806 * node count for the tree. 74807 * @property _nodes 74808 * @type Node[] 74809 * @private 74810 */ 74811 _nodes: null, 74812 74813 /** 74814 * We lock the tree control while waiting for the dynamic loader to return 74815 * @property locked 74816 * @type boolean 74817 */ 74818 locked: false, 74819 74820 /** 74821 * The animation to use for expanding children, if any 74822 * @property _expandAnim 74823 * @type string 74824 * @private 74825 */ 74826 _expandAnim: null, 74827 74828 /** 74829 * The animation to use for collapsing children, if any 74830 * @property _collapseAnim 74831 * @type string 74832 * @private 74833 */ 74834 _collapseAnim: null, 74835 74836 /** 74837 * The current number of animations that are executing 74838 * @property _animCount 74839 * @type int 74840 * @private 74841 */ 74842 _animCount: 0, 74843 74844 /** 74845 * The maximum number of animations to run at one time. 74846 * @property maxAnim 74847 * @type int 74848 */ 74849 maxAnim: 2, 74850 74851 /** 74852 * Whether there is any subscriber to dblClickEvent 74853 * @property _hasDblClickSubscriber 74854 * @type boolean 74855 * @private 74856 */ 74857 _hasDblClickSubscriber: false, 74858 74859 /** 74860 * Stores the timer used to check for double clicks 74861 * @property _dblClickTimer 74862 * @type window.timer object 74863 * @private 74864 */ 74865 _dblClickTimer: null, 74866 74867 /** 74868 * A reference to the Node currently having the focus or null if none. 74869 * @property currentFocus 74870 * @type YAHOO.widget.Node 74871 */ 74872 currentFocus: null, 74873 74874 /** 74875 * If true, only one Node can be highlighted at a time 74876 * @property singleNodeHighlight 74877 * @type boolean 74878 * @default false 74879 */ 74880 74881 singleNodeHighlight: false, 74882 74883 /** 74884 * A reference to the Node that is currently highlighted. 74885 * It is only meaningful if singleNodeHighlight is enabled 74886 * @property _currentlyHighlighted 74887 * @type YAHOO.widget.Node 74888 * @default null 74889 * @private 74890 */ 74891 74892 _currentlyHighlighted: null, 74893 74894 /** 74895 * Sets up the animation for expanding children 74896 * @method setExpandAnim 74897 * @param {string} type the type of animation (acceptable values defined 74898 * in YAHOO.widget.TVAnim) 74899 */ 74900 setExpandAnim: function(type) { 74901 this._expandAnim = (Widget.TVAnim.isValid(type)) ? type : null; 74902 }, 74903 74904 /** 74905 * Sets up the animation for collapsing children 74906 * @method setCollapseAnim 74907 * @param {string} type of animation (acceptable values defined in 74908 * YAHOO.widget.TVAnim) 74909 */ 74910 setCollapseAnim: function(type) { 74911 this._collapseAnim = (Widget.TVAnim.isValid(type)) ? type : null; 74912 }, 74913 74914 /** 74915 * Perform the expand animation if configured, or just show the 74916 * element if not configured or too many animations are in progress 74917 * @method animateExpand 74918 * @param el {HTMLElement} the element to animate 74919 * @param node {YAHOO.util.Node} the node that was expanded 74920 * @return {boolean} true if animation could be invoked, false otherwise 74921 */ 74922 animateExpand: function(el, node) { 74923 this.logger.log("animating expand"); 74924 74925 if (this._expandAnim && this._animCount < this.maxAnim) { 74926 // this.locked = true; 74927 var tree = this; 74928 var a = Widget.TVAnim.getAnim(this._expandAnim, el, 74929 function() { tree.expandComplete(node); }); 74930 if (a) { 74931 ++this._animCount; 74932 this.fireEvent("animStart", { 74933 "node": node, 74934 "type": "expand" 74935 }); 74936 a.animate(); 74937 } 74938 74939 return true; 74940 } 74941 74942 return false; 74943 }, 74944 74945 /** 74946 * Perform the collapse animation if configured, or just show the 74947 * element if not configured or too many animations are in progress 74948 * @method animateCollapse 74949 * @param el {HTMLElement} the element to animate 74950 * @param node {YAHOO.util.Node} the node that was expanded 74951 * @return {boolean} true if animation could be invoked, false otherwise 74952 */ 74953 animateCollapse: function(el, node) { 74954 this.logger.log("animating collapse"); 74955 74956 if (this._collapseAnim && this._animCount < this.maxAnim) { 74957 // this.locked = true; 74958 var tree = this; 74959 var a = Widget.TVAnim.getAnim(this._collapseAnim, el, 74960 function() { tree.collapseComplete(node); }); 74961 if (a) { 74962 ++this._animCount; 74963 this.fireEvent("animStart", { 74964 "node": node, 74965 "type": "collapse" 74966 }); 74967 a.animate(); 74968 } 74969 74970 return true; 74971 } 74972 74973 return false;
74974 }, 74975 74976 /** 74977 * Function executed when the expand animation completes 74978 * @method expandComplete 74979 */ 74980 expandComplete: function(node) { 74981 this.logger.log("expand complete: " + this.id); 74982 --this._animCount; 74983 this.fireEvent("animComplete", { 74984 "node": node, 74985 "type": "expand" 74986 }); 74987 // this.locked = false; 74988 }, 74989 74990 /** 74991 * Function executed when the collapse animation completes 74992 * @method collapseComplete 74993 */ 74994 collapseComplete: function(node) { 74995 this.logger.log("collapse complete: " + this.id); 74996 --this._animCount; 74997 this.fireEvent("animComplete", { 74998 "node": node, 74999 "type": "collapse" 75000 }); 75001 // this.locked = false; 75002 }, 75003 75004 /** 75005 * Initializes the tree 75006 * @method init 75007 * @parm {string|HTMLElement} id the id of the element that will hold the tree 75008 * @private 75009 */ 75010 init: function(id) { 75011 this._el = Dom.get(id); 75012 this.id = Dom.generateId(this._el,"yui-tv-auto-id-"); 75013 75014 /** 75015 * When animation is enabled, this event fires when the animation 75016 * starts 75017 * @event animStart 75018 * @type CustomEvent 75019 * @param {YAHOO.widget.Node} oArgs.node the node that is expanding/collapsing 75020 * @param {String} oArgs.type the type of animation ("expand" or "collapse") 75021 */ 75022 this.createEvent("animStart", this); 75023 75024 /** 75025 * When animation is enabled, this event fires when the animation 75026 * completes 75027 * @event animComplete 75028 * @type CustomEvent 75029 * @param {YAHOO.widget.Node} oArgs.node the node that is expanding/collapsing 75030 * @param {String} oArgs.type the type of animation ("expand" or "collapse") 75031 */ 75032 this.createEvent("animComplete", this); 75033 75034 /** 75035 * Fires when a node is going to be collapsed. Return false to stop 75036 * the collapse. 75037 * @event collapse 75038 * @type CustomEvent 75039 * @param {YAHOO.widget.Node} node the node that is collapsing 75040 */ 75041 this.createEvent("collapse", this); 75042 75043 /** 75044 * Fires after a node is successfully collapsed. This event will not fire 75045 * if the "collapse" event was cancelled. 75046 * @event collapseComplete 75047 * @type CustomEvent 75048 * @param {YAHOO.widget.Node} node the node that was collapsed 75049 */ 75050 this.createEvent("collapseComplete", this); 75051 75052 /** 75053 * Fires when a node is going to be expanded. Return false to stop 75054 * the collapse. 75055 * @event expand 75056 * @type CustomEvent 75057 * @param {YAHOO.widget.Node} node the node that is expanding 75058 */ 75059 this.createEvent("expand", this); 75060 75061 /** 75062 * Fires after a node is successfully expanded. This event will not fire 75063 * if the "expand" event was cancelled. 75064 * @event expandComplete 75065 * @type CustomEvent 75066 * @param {YAHOO.widget.Node} node the node that was expanded 75067 */ 75068 this.createEvent("expandComplete", this); 75069 75070 /** 75071 * Fires when the Enter key is pressed on a node that has the focus 75072 * @event enterKeyPressed 75073 * @type CustomEvent 75074 * @param {YAHOO.widget.Node} node the node that has the focus 75075 */ 75076 this.createEvent("enterKeyPressed", this); 75077 75078 /** 75079 * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a Click. 75080 * The listener may return false to cancel toggling and focusing on the node. 75081 * @event clickEvent 75082 * @type CustomEvent 75083 * @param oArgs.event {HTMLEvent} The event object 75084 * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked 75085 */ 75086 this.createEvent("clickEvent", this); 75087 75088 /** 75089 * Fires when the focus receives the focus, when it changes from a Node 75090 * to another Node or when it is completely lost (blurred) 75091 * @event focusChanged 75092 * @type CustomEvent 75093 * @param oArgs.oldNode {YAHOO.widget.Node} Node that had the focus or null if none 75094 * @param oArgs.newNode {YAHOO.widget.Node} Node that receives the focus or null if none 75095 */ 75096 75097 this.createEvent('focusChanged',this); 75098 75099 /** 75100 * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a double Click 75101 * @event dblClickEvent 75102 * @type CustomEvent 75103 * @param oArgs.event {HTMLEvent} The event object 75104 * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked 75105 */ 75106 var self = this; 75107 this.createEvent("dblClickEvent", { 75108 scope:this, 75109 onSubscribeCallback: function() {
75110 self._hasDblClickSubscriber = true; 75111 } 75112 }); 75113 75114 /** 75115 * Custom event that is fired when the text node label is clicked. 75116 * The node clicked is provided as an argument 75117 * 75118 * @event labelClick 75119 * @type CustomEvent 75120 * @param {YAHOO.widget.Node} node the node clicked 75121 * @deprecated use clickEvent or dblClickEvent 75122 */ 75123 this.createEvent("labelClick", this); 75124 75125 /** 75126 * Custom event fired when the highlight of a node changes. 75127 * The node that triggered the change is provided as an argument: 75128 * The status of the highlight can be checked in 75129 * <a href="YAHOO.widget.Node.html#property_highlightState">nodeRef.highlightState</a>. 75130 * Depending on <a href="YAHOO.widget.Node.html#property_propagateHighlight">nodeRef.propagateHighlight</a>, other nodes might have changed 75131 * @event highlightEvent 75132 * @type CustomEvent 75133 * @param node {YAHOO.widget.Node} the node that started the change in highlighting state 75134 */ 75135 this.createEvent("highlightEvent",this); 75136 75137 75138 this._nodes = []; 75139 75140 // store a global reference 75141 TV.trees[this.id] = this; 75142 75143 // Set up the root node 75144 this.root = new Widget.RootNode(this); 75145 75146 var LW = Widget.LogWriter; 75147 75148 this.logger = (LW) ? new LW(this.toString()) : YAHOO; 75149 75150 this.logger.log("tree init: " + this.id); 75151 75152 if (this._initEditor) { 75153 this._initEditor(); 75154 } 75155 75156 // YAHOO.util.Event.onContentReady(this.id, this.handleAvailable, this, true); 75157 // YAHOO.util.Event.on(this.id, "click", this.handleClick, this, true); 75158 }, 75159 75160 //handleAvailable: function() { 75161 //var Event = YAHOO.util.Event; 75162 //Event.on(this.id, 75163 //}, 75164 /** 75165 * Builds the TreeView from an object. 75166 * This is the method called by the constructor to build the tree when it has a second argument. 75167 * A tree can be described by an array of objects, each object corresponding to a node. 75168 * Node descriptions may contain values for any property of a node plus the following extra properties: <ul> 75169 * <li>type: can be one of the following:<ul> 75170 * <li> A shortname for a node type (<code>'text','menu','html'</code>) </li> 75171 * <li>The name of a Node class under YAHOO.widget (<code>'TextNode', 'MenuNode', 'DateNode'</code>, etc) </li> 75172 * <li>a reference to an actual class: <code>YAHOO.widget.DateNode</code></li> 75173 * </ul></li> 75174 * <li>children: an array containing further node definitions</li></ul> 75175 * A string instead of an object will produce a node of type 'text' with the given string as its label. 75176 * @method buildTreeFromObject 75177 * @param oConfig {Array|Object|String} array containing a full description of the tree. 75178 * An object or a string will be turned into an array with the given object or string as its only element. 75179 * 75180 */ 75181 buildTreeFromObject: function (oConfig) { 75182 var logger = this.logger; 75183 logger.log('Building tree from object'); 75184 var build = function (parent, oConfig) { 75185 var i, item, node, children, type, NodeType, ThisType; 75186 for (i = 0; i < oConfig.length; i++) { 75187 item = oConfig[i]; 75188 if (Lang.isString(item)) { 75189 node = new Widget.TextNode(item, parent); 75190 } else if (Lang.isObject(item)) { 75191 children = item.children; 75192 delete item.children; 75193 type = item.type || 'text'; 75194 delete item.type; 75195 switch (Lang.isString(type) && type.toLowerCase()) { 75196 case 'text': 75197 node = new Widget.TextNode(item, parent); 75198 break; 75199 case 'menu': 75200 node = new Widget.MenuNode(item, parent); 75201 break; 75202 case 'html': 75203 node = new Widget.HTMLNode(item, parent); 75204 break; 75205 default: 75206 if (Lang.isString(type)) { 75207 NodeType = Widget[type]; 75208 } else { 75209 NodeType = type; 75210 } 75211 if (Lang.isObject(NodeType)) { 75212 for (ThisType = NodeType; ThisType && ThisType !== Widget.Node; ThisType = ThisType.superclass.constructor) {} 75213 if (ThisType) { 75214 node = new NodeType(item, parent); 75215 } else { 75216 logger.log('Invalid type in node definition: ' + type,'error'); 75217 } 75218 } else { 75219 logger.log('Invalid type in node definition: ' + type,'error'); 75220 } 75221 } 75222 if (children) { 75223 build(node,children); 75224 } 75225 } else { 75226 logger.log('Invalid node definition','error'); 75227 } 75228 } 75229 }; 75230 if (!Lang.isArray(oConfig)) { 75231 oConfig = [oConfig]; 75232 } 75233 75234 75235 build(this.root,oConfig); 75236 }, 75237/** 75238 * Builds the TreeView from existing markup. Markup should consist of <UL> or <OL> elements containing <LI> elements. 75239 * Each <LI> can have one element used as label and a second optional element which is to be a <UL> or <OL> 75240 * containing nested nodes.
75241 * Depending on what the first element of the <LI> element is, the following Nodes will be created: <ul> 75242 * <li>plain text: a regular TextNode</li> 75243 * <li>anchor <A>: a TextNode with its <code>href</code> and <code>target</code> taken from the anchor</li> 75244 * <li>anything else: an HTMLNode</li></ul> 75245 * Only the first outermost (un-)ordered list in the markup and its children will be parsed. 75246 * Nodes will be collapsed unless an <LI> tag has a className called 'expanded'. 75247 * All other className attributes will be copied over to the Node className property. 75248 * If the <LI> element contains an attribute called <code>yuiConfig</code>, its contents should be a JSON-encoded object 75249 * as the one used in method <a href="#method_buildTreeFromObject">buildTreeFromObject</a>. 75250 * @method buildTreeFromMarkup 75251 * @param id {string|HTMLElement} The id of the element that contains the markup or a reference to it. 75252 */ 75253 buildTreeFromMarkup: function (id) { 75254 this.logger.log('Building tree from existing markup'); 75255 var build = function (markup) { 75256 var el, child, branch = [], config = {}, label, yuiConfig; 75257 // Dom's getFirstChild and getNextSibling skip over text elements 75258 for (el = Dom.getFirstChild(markup); el; el = Dom.getNextSibling(el)) { 75259 switch (el.tagName.toUpperCase()) { 75260 case 'LI': 75261 label = ''; 75262 config = { 75263 expanded: Dom.hasClass(el,'expanded'), 75264 title: el.title || el.alt || null, 75265 className: Lang.trim(el.className.replace(/\bexpanded\b/,'')) || null 75266 }; 75267 // I cannot skip over text elements here because I want them for labels 75268 child = el.firstChild; 75269 if (child.nodeType == 3) { 75270 // nodes with only whitespace, tabs and new lines don't count, they are probably just formatting. 75271 label = Lang.trim(child.nodeValue.replace(/[\n\t\r]*/g,'')); 75272 if (label) { 75273 config.type = 'text'; 75274 config.label = label; 75275 } else { 75276 child = Dom.getNextSibling(child); 75277 } 75278 } 75279 if (!label) { 75280 if (child.tagName.toUpperCase() == 'A') { 75281 config.type = 'text'; 75282 config.label = child.innerHTML; 75283 config.href = child.href; 75284 config.target = child.target; 75285 config.title = child.title || child.alt || config.title; 75286 } else { 75287 config.type = 'html'; 75288 var d = document.createElement('div'); 75289 d.appendChild(child.cloneNode(true)); 75290 config.html = d.innerHTML; 75291 config.hasIcon = true; 75292 } 75293 } 75294 // see if after the label it has a further list which will become children of this node. 75295 child = Dom.getNextSibling(child); 75296 switch (child && child.tagName.toUpperCase()) { 75297 case 'UL': 75298 case 'OL': 75299 config.children = build(child); 75300 break; 75301 } 75302 // if there are further elements or text, it will be ignored. 75303 75304 if (YAHOO.lang.JSON) { 75305 yuiConfig = el.getAttribute('yuiConfig'); 75306 if (yuiConfig) { 75307 yuiConfig = YAHOO.lang.JSON.parse(yuiConfig); 75308 config = YAHOO.lang.merge(config,yuiConfig); 75309 } 75310 } 75311 75312 branch.push(config); 75313 break; 75314 case 'UL': 75315 case 'OL': 75316 this.logger.log('ULs or OLs can only contain LI elements, not other UL or OL. This will not work in some browsers','error'); 75317 config = { 75318 type: 'text', 75319 label: '', 75320 children: build(child) 75321 }; 75322 branch.push(config); 75323 break; 75324 } 75325 } 75326 return branch; 75327 }; 75328 75329 var markup = Dom.getChildrenBy(Dom.get(id),function (el) { 75330 var tag = el.tagName.toUpperCase(); 75331 return tag == 'UL' || tag == 'OL'; 75332 }); 75333 if (markup.length) { 75334 this.buildTreeFromObject(build(markup[0])); 75335 } else {
75336 this.logger.log('Markup contains no UL or OL elements','warn'); 75337 } 75338 }, 75339 /** 75340 * Returns the TD element where the event has occurred 75341 * @method _getEventTargetTdEl 75342 * @private 75343 */ 75344 _getEventTargetTdEl: function (ev) { 75345 var target = Event.getTarget(ev); 75346 // go up looking for a TD with a className with a ygtv prefix 75347 while (target && !(target.tagName.toUpperCase() == 'TD' && Dom.hasClass(target.parentNode,'ygtvrow'))) { 75348 target = Dom.getAncestorByTagName(target,'td'); 75349 } 75350 if (Lang.isNull(target)) { return null; } 75351 // If it is a spacer cell, do nothing 75352 if (/\bygtv(blank)?depthcell/.test(target.className)) { return null;} 75353 // If it has an id, search for the node number and see if it belongs to a node in this tree. 75354 if (target.id) { 75355 var m = target.id.match(/\bygtv([^\d]*)(.*)/); 75356 if (m && m[2] && this._nodes[m[2]]) { 75357 return target; 75358 } 75359 } 75360 return null; 75361 }, 75362 /** 75363 * Event listener for click events 75364 * @method _onClickEvent 75365 * @private 75366 */ 75367 _onClickEvent: function (ev) { 75368 var self = this, 75369 td = this._getEventTargetTdEl(ev), 75370 node, 75371 target, 75372 toggle = function (force) { 75373 node.focus(); 75374 if (force || !node.href) { 75375 node.toggle(); 75376 try { 75377 Event.preventDefault(ev); 75378 } catch (e) { 75379 // @TODO 75380 // For some reason IE8 is providing an event object with 75381 // most of the fields missing, but only when clicking on 75382 // the node's label, and only when working with inline 75383 // editing. This generates a "Member not found" error 75384 // in that browser. Determine if this is a browser 75385 // bug, or a problem with this code. Already checked to 75386 // see if the problem has to do with access the event 75387 // in the outer scope, and that isn't the problem. 75388 // Maybe the markup for inline editing is broken. 75389 } 75390 } 75391 }; 75392 75393 if (!td) { 75394 return; 75395 } 75396 75397 node = this.getNodeByElement(td); 75398 if (!node) { 75399 return; 75400 } 75401 75402 // exception to handle deprecated event labelClick 75403 // @TODO take another look at this deprecation. It is common for people to 75404 // only be interested in the label click, so why make them have to test 75405 // the node type to figure out whether the click was on the label? 75406 target = Event.getTarget(ev); 75407 if (Dom.hasClass(target, node.labelStyle) || Dom.getAncestorByClassName(target,node.labelStyle)) { 75408 this.logger.log("onLabelClick " + node.label); 75409 this.fireEvent('labelClick',node); 75410 } 75411 // http://yuilibrary.com/projects/yui2/ticket/2528946 75412 // Ensures that any open editor is closed. 75413 // Since the editor is in a separate source which might not be included, 75414 // we first need to ensure we have the _closeEditor method available 75415 if (this._closeEditor) { this._closeEditor(false); } 75416 75417 // If it is a toggle cell, toggle 75418 if (/\bygtv[tl][mp]h?h?/.test(td.className)) { 75419 toggle(true); 75420 } 75421 //Start Changes 75422 else if (node._customRegEx(node.customCls) && node._customRegEx(node.customCls).test(td.className)) 75423 { 75424 toggle(true); 75425 } 75426 //End Changes 75427 else { 75428 if (this._dblClickTimer) { 75429 window.clearTimeout(this._dblClickTimer); 75430 this._dblClickTimer = null; 75431 } else { 75432 if (this._hasDblClickSubscriber) { 75433 this._dblClickTimer = window.setTimeout(function () { 75434 self._dblClickTimer = null; 75435 if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) { 75436 toggle(); 75437 } 75438 }, 200); 75439 } else { 75440 if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) { 75441 toggle(); 75442 } 75443 } 75444 } 75445 } 75446 }, 75447 75448 /** 75449 * Event listener for double-click events 75450 * @method _onDblClickEvent 75451 * @private 75452 */ 75453 _onDblClickEvent: function (ev) { 75454 if (!this._hasDblClickSubscriber) { return; } 75455 var td = this._getEventTargetTdEl(ev); 75456 if (!td) {return;} 75457 75458 if (!(/\bygtv[tl][mp]h?h?/.test(td.className))) { 75459 this.fireEvent('dblClickEvent', {event:ev, node:this.getNodeByElement(td)}); 75460 if (this._dblClickTimer) { 75461 window.clearTimeout(this._dblClickTimer); 75462 this._dblClickTimer = null; 75463 } 75464 } 75465 }, 75466 /** 75467 * Event listener for mouse over events 75468 * @method _onMouseOverEvent 75469 * @private 75470 */ 75471 _onMouseOverEvent:function (ev) { 75472 var target; 75473 if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) { 75474 target.className = target.className.replace(/\bygtv([lt])([mp])\b/gi,'ygtv$1$2h'); 75475 } 75476 }, 75477 /** 75478 * Event listener for mouse out events 75479 * @method _onMouseOutEvent 75480 * @private 75481 */ 75482 _onMouseOutEvent: function (ev) { 75483 var target; 75484 if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) { 75485 target.className = target.className.replace(/\bygtv([lt])([mp])h\b/gi,'ygtv$1$2'); 75486 } 75487 }, 75488 /** 75489 * Event listener for key down events 75490 * @method _onKeyDownEvent 75491 * @private 75492 */ 75493 _onKeyDownEvent: function (ev) { 75494 var target = Event.getTarget(ev), 75495 node = this.getNodeByElement(target), 75496 newNode = node, 75497 KEY = YAHOO.util.KeyListener.KEY; 75498 75499 switch(ev.keyCode) { 75500 case KEY.UP: 75501 this.logger.log('UP'); 75502 do { 75503 if (newNode.previousSibling) { 75504 newNode = newNode.previousSibling; 75505 } else { 75506 newNode = newNode.parent; 75507 } 75508 } while (newNode && !newNode._canHaveFocus()); 75509 if (newNode) { newNode.focus(); } 75510 Event.preventDefault(ev); 75511 break; 75512 case KEY.DOWN: 75513 this.logger.log('DOWN'); 75514 do { 75515 if (newNode.nextSibling) { 75516 newNode = newNode.nextSibling; 75517 } else { 75518 newNode.expand(); 75519 newNode = (newNode.children.length || null) && newNode.children[0]; 75520 } 75521 } while (newNode && !newNode._canHaveFocus); 75522 if (newNode) { newNode.focus();} 75523 Event.preventDefault(ev); 75524 break; 75525 case KEY.LEFT: 75526 this.logger.log('LEFT'); 75527 do { 75528 if (newNode.parent) { 75529 newNode = newNode.parent; 75530 } else { 75531 newNode = newNode.previousSibling; 75532 } 75533 } while (newNode && !newNode._canHaveFocus()); 75534 if (newNode) { newNode.focus();} 75535 Event.preventDefault(ev); 75536 break; 75537 case KEY.RIGHT: 75538 this.logger.log('RIGHT'); 75539 var self = this, 75540 moveFocusRight, 75541 focusOnExpand = function (newNode) { 75542 self.unsubscribe('expandComplete',focusOnExpand); 75543 moveFocusRight(newNode); 75544 }; 75545 moveFocusRight = function (newNode) { 75546 do { 75547 if (newNode.isDynamic() && !newNode.childrenRendered) { 75548 self.subscribe('expandComplete',focusOnExpand); 75549 newNode.expand(); 75550 newNode = null; 75551 break; 75552 } else { 75553 newNode.expand(); 75554 if (newNode.children.length) { 75555 newNode = newNode.children[0]; 75556 } else { 75557 newNode = newNode.nextSibling; 75558 } 75559 } 75560 } while (newNode && !newNode._canHaveFocus()); 75561 if (newNode) { newNode.focus();} 75562 }; 75563 75564 moveFocusRight(newNode); 75565 Event.preventDefault(ev); 75566 break; 75567 case KEY.ENTER: 75568 this.logger.log('ENTER: ' + newNode.href); 75569 if (node.href) { 75570 if (node.target) { 75571 window.open(node.href,node.target); 75572 } else { 75573 window.location(node.href); 75574 } 75575 } else {
75576 node.toggle(); 75577 } 75578 this.fireEvent('enterKeyPressed',node); 75579 Event.preventDefault(ev); 75580 break; 75581 case KEY.HOME: 75582 this.logger.log('HOME'); 75583 newNode = this.getRoot(); 75584 if (newNode.children.length) {newNode = newNode.children[0];} 75585 if (newNode._canHaveFocus()) { newNode.focus(); } 75586 Event.preventDefault(ev); 75587 break; 75588 case KEY.END: 75589 this.logger.log('END'); 75590 newNode = newNode.parent.children; 75591 newNode = newNode[newNode.length -1]; 75592 if (newNode._canHaveFocus()) { newNode.focus(); } 75593 Event.preventDefault(ev); 75594 break; 75595 // case KEY.PAGE_UP: 75596 // this.logger.log('PAGE_UP'); 75597 // break; 75598 // case KEY.PAGE_DOWN: 75599 // this.logger.log('PAGE_DOWN'); 75600 // break; 75601 case 107: // plus key 75602 case 187: // plus key 75603 if (ev.shiftKey) { 75604 this.logger.log('Shift-PLUS'); 75605 node.parent.expandAll(); 75606 } else { 75607 this.logger.log('PLUS'); 75608 node.expand(); 75609 } 75610 break; 75611 case 109: // minus key 75612 case 189: // minus key 75613 if (ev.shiftKey) { 75614 this.logger.log('Shift-MINUS'); 75615 node.parent.collapseAll(); 75616 } else { 75617 this.logger.log('MINUS'); 75618 node.collapse(); 75619 } 75620 break; 75621 default: 75622 break; 75623 } 75624 }, 75625 /** 75626 * Renders the tree boilerplate and visible nodes 75627 * @method render 75628 */ 75629 render: function() { 75630 var html = this.root.getHtml(), 75631 el = this.getEl(); 75632 el.innerHTML = html; 75633 if (!this._hasEvents) { 75634 Event.on(el, 'click', this._onClickEvent, this, true); 75635 Event.on(el, 'dblclick', this._onDblClickEvent, this, true); 75636 Event.on(el, 'mouseover', this._onMouseOverEvent, this, true); 75637 Event.on(el, 'mouseout', this._onMouseOutEvent, this, true); 75638 Event.on(el, 'keydown', this._onKeyDownEvent, this, true); 75639 } 75640 this._hasEvents = true; 75641 }, 75642 75643 /** 75644 * Returns the tree's host element 75645 * @method getEl 75646 * @return {HTMLElement} the host element 75647 */ 75648 getEl: function() { 75649 if (! this._el) { 75650 this._el = Dom.get(this.id); 75651 } 75652 return this._el; 75653 }, 75654 75655 /** 75656 * Nodes register themselves with the tree instance when they are created. 75657 * @method regNode 75658 * @param node {Node} the node to register 75659 * @private 75660 */ 75661 regNode: function(node) { 75662 this._nodes[node.index] = node; 75663 }, 75664 75665 /** 75666 * Returns the root node of this tree 75667 * @method getRoot 75668 * @return {Node} the root node 75669 */ 75670 getRoot: function() { 75671 return this.root; 75672 }, 75673 75674 /** 75675 * Configures this tree to dynamically load all child data 75676 * @method setDynamicLoad 75677 * @param {function} fnDataLoader the function that will be called to get the data 75678 * @param iconMode {int} configures the icon that is displayed when a dynamic 75679 * load node is expanded the first time without children. By default, the 75680 * "collapse" icon will be used. If set to 1, the leaf node icon will be 75681 * displayed. 75682 */ 75683 setDynamicLoad: function(fnDataLoader, iconMode) { 75684 this.root.setDynamicLoad(fnDataLoader, iconMode); 75685 }, 75686 75687 /** 75688 * Expands all child nodes. Note: this conflicts with the "multiExpand" 75689 * node property. If expand all is called in a tree with nodes that 75690 * do not allow multiple siblings to be displayed, only the last sibling 75691 * will be expanded. 75692 * @method expandAll 75693 */ 75694 expandAll: function() { 75695 if (!this.locked) { 75696 this.root.expandAll(); 75697 } 75698 }, 75699 75700 /** 75701 * Collapses all expanded child nodes in the entire tree. 75702 * @method collapseAll 75703 */ 75704 collapseAll: function() { 75705 if (!this.locked) { 75706 this.root.collapseAll(); 75707 } 75708 }, 75709 75710 /** 75711 * Returns a node in the tree that has the specified index (this index 75712 * is created internally, so this function probably will only be used 75713 * in html generated for a given node.) 75714 * @method getNodeByIndex 75715 * @param {int} nodeIndex the index of the node wanted 75716 * @return {Node} the node with index=nodeIndex, null if no match 75717 */ 75718 getNodeByIndex: function(nodeIndex) { 75719 var n = this._nodes[nodeIndex]; 75720 return (n) ? n : null; 75721 }, 75722 75723 /** 75724 * Returns a node that has a matching property and value in the data 75725 * object that was passed into its constructor. 75726 * @method getNodeByProperty 75727 * @param {object} property the property to search (usually a string) 75728 * @param {object} value the value we want to find (usuall an int or string) 75729 * @return {Node} the matching node, null if no match 75730 */ 75731 getNodeByProperty: function(property, value) { 75732 for (var i in this._nodes) { 75733 if (this._nodes.hasOwnProperty(i)) { 75734 var n = this._nodes[i]; 75735 if ((property in n && n[property] == value) || (n.data && value == n.data[property])) { 75736 return n; 75737 } 75738 } 75739 } 75740 75741 return null; 75742 }, 75743 75744 /** 75745 * Returns a collection of nodes that have a matching property 75746 * and value in the data object that was passed into its constructor. 75747 * @method getNodesByProperty 75748 * @param {object} property the property to search (usually a string) 75749 * @param {object} value the value we want to find (usuall an int or string) 75750 * @return {Array} the matching collection of nodes, null if no match 75751 */ 75752 getNodesByProperty: function(property, value) { 75753 var values = []; 75754 for (var i in this._nodes) { 75755 if (this._nodes.hasOwnProperty(i)) { 75756 var n = this._nodes[i]; 75757 if ((property in n && n[property] == value) || (n.data && value == n.data[property])) { 75758 values.push(n); 75759 } 75760 } 75761 } 75762 75763 return (values.length) ? values : null; 75764 }, 75765 75766 75767 /** 75768 * Returns a collection of nodes that have passed the test function 75769 * passed as its only argument. 75770 * The function will receive a reference to each node to be tested. 75771 * @method getNodesBy 75772 * @param {function} a boolean function that receives a Node instance and returns true to add the node to the results list 75773 * @return {Array} the matching collection of nodes, null if no match 75774 */ 75775 getNodesBy: function(fn) { 75776 var values = []; 75777 for (var i in this._nodes) { 75778 if (this._nodes.hasOwnProperty(i)) { 75779 var n = this._nodes[i]; 75780 if (fn(n)) { 75781 values.push(n); 75782 } 75783 } 75784 } 75785 return (values.length) ? values : null; 75786 }, 75787 /** 75788 * Returns the treeview node reference for an ancestor element 75789 * of the node, or null if it is not contained within any no
75789de 75790 * in this tree. 75791 * @method getNodeByElement 75792 * @param el {HTMLElement} the element to test 75793 * @return {YAHOO.widget.Node} a node reference or null 75794 */ 75795 getNodeByElement: function(el) { 75796 75797 var p=el, m, re=/ygtv([^\d]*)(.*)/; 75798 75799 do { 75800 75801 if (p && p.id) { 75802 m = p.id.match(re); 75803 if (m && m[2]) { 75804 return this.getNodeByIndex(m[2]); 75805 } 75806 } 75807 75808 p = p.parentNode; 75809 75810 if (!p || !p.tagName) { 75811 break; 75812 } 75813 75814 } 75815 while (p.id !== this.id && p.tagName.toLowerCase() !== "body"); 75816 75817 return null; 75818 }, 75819 75820 /** 75821 * When in singleNodeHighlight it returns the node highlighted 75822 * or null if none. Returns null if singleNodeHighlight is false. 75823 * @method getHighlightedNode 75824 * @return {YAHOO.widget.Node} a node reference or null 75825 */ 75826 getHighlightedNode: function() { 75827 return this._currentlyHighlighted; 75828 }, 75829 75830 75831 /** 75832 * Removes the node and its children, and optionally refreshes the 75833 * branch of the tree that was affected. 75834 * @method removeNode 75835 * @param {Node} node to remove 75836 * @param {boolean} autoRefresh automatically refreshes branch if true 75837 * @return {boolean} False is there was a problem, true otherwise. 75838 */ 75839 removeNode: function(node, autoRefresh) { 75840 75841 // Don't delete the root node 75842 if (node.isRoot()) { 75843 return false; 75844 } 75845 75846 // Get the branch that we may need to refresh 75847 var p = node.parent; 75848 if (p.parent) { 75849 p = p.parent; 75850 } 75851 75852 // Delete the node and its children 75853 this._deleteNode(node); 75854 75855 // Refresh the parent of the parent 75856 if (autoRefresh && p && p.childrenRendered) { 75857 p.refresh(); 75858 } 75859 75860 return true; 75861 }, 75862 75863 /** 75864 * wait until the animation is complete before deleting 75865 * to avoid javascript errors 75866 * @method _removeChildren_animComplete 75867 * @param o the custom event payload 75868 * @private 75869 */ 75870 _removeChildren_animComplete: function(o) { 75871 this.unsubscribe(this._removeChildren_animComplete); 75872 this.removeChildren(o.node); 75873 }, 75874 75875 /** 75876 * Deletes this nodes child collection, recursively. Also collapses 75877 * the node, and resets the dynamic load flag. The primary use for 75878 * this method is to purge a node and allow it to fetch its data 75879 * dynamically again. 75880 * @method removeChildren 75881 * @param {Node} node the node to purge 75882 */ 75883 removeChildren: function(node) { 75884 75885 if (node.expanded) { 75886 // wait until the animation is complete before deleting to 75887 // avoid javascript errors 75888 if (this._collapseAnim) { 75889 this.subscribe("animComplete", 75890 this._removeChildren_animComplete, this, true); 75891 Widget.Node.prototype.collapse.call(node); 75892 return; 75893 } 75894 75895 node.collapse(); 75896 } 75897 75898 this.logger.log("Removing children for " + node); 75899 while (node.children.length) { 75900 this._deleteNode(node.children[0]); 75901 } 75902 75903 if (node.isRoot()) { 75904 Widget.Node.prototype.expand.call(node); 75905 } 75906 75907 node.childrenRendered = false; 75908 node.dynamicLoadComplete = false; 75909 75910 node.updateIcon(); 75911 }, 75912 75913 /** 75914 * Deletes the node and recurses children 75915 * @method _deleteNode 75916 * @private 75917 */ 75918 _deleteNode: function(node) { 75919 // Remove all the child nodes first 75920 this.removeChildren(node); 75921 75922 // Remove the node from the tree 75923 this.popNode(node); 75924 }, 75925 75926 /** 75927 * Removes the node from the tree, preserving the child collection 75928 * to make it possible to insert the branch into another part of the 75929 * tree, or another tree. 75930 * @method popNode 75931 * @param {Node} node to remove 75932 */ 75933 popNode: function(node) { 75934 var p = node.parent; 75935 75936 // Update the parent's collection of children 75937 var a = []; 75938 75939 for (var i=0, len=p.children.length;i<len;++i) { 75940 if (p.children[i] != node) { 75941 a[a.length] = p.children[i]; 75942 } 75943 } 75944 75945 p.children = a; 75946 75947 // reset the childrenRendered flag for the parent 75948 p.childrenRendered = false;
75949 75950 // Update the sibling relationship 75951 if (node.previousSibling) { 75952 node.previousSibling.nextSibling = node.nextSibling; 75953 } 75954 75955 if (node.nextSibling) { 75956 node.nextSibling.previousSibling = node.previousSibling; 75957 } 75958 75959 if (this.currentFocus == node) { 75960 this.currentFocus = null; 75961 } 75962 if (this._currentlyHighlighted == node) { 75963 this._currentlyHighlighted = null; 75964 } 75965 75966 node.parent = null; 75967 node.previousSibling = null; 75968 node.nextSibling = null; 75969 node.tree = null; 75970 75971 // Update the tree's node collection 75972 delete this._nodes[node.index]; 75973 }, 75974 75975 /** 75976 * Nulls out the entire TreeView instance and related objects, removes attached 75977 * event listeners, and clears out DOM elements inside the container. After 75978 * calling this method, the instance reference should be expliclitly nulled by 75979 * implementer, as in myDataTable = null. Use with caution! 75980 * 75981 * @method destroy 75982 */ 75983 destroy : function() { 75984 // Since the label editor can be separated from the main TreeView control 75985 // the destroy method for it might not be there. 75986 if (this._destroyEditor) { this._destroyEditor(); } 75987 var el = this.getEl(); 75988 Event.removeListener(el,'click'); 75989 Event.removeListener(el,'dblclick'); 75990 Event.removeListener(el,'mouseover'); 75991 Event.removeListener(el,'mouseout'); 75992 Event.removeListener(el,'keydown'); 75993 for (var i = 0 ; i < this._nodes.length; i++) { 75994 var node = this._nodes[i]; 75995 if (node && node.destroy) {node.destroy(); } 75996 } 75997 el.innerHTML = ''; 75998 this._hasEvents = false; 75999 }, 76000 76001 76002 76003 76004 /** 76005 * TreeView instance toString 76006 * @method toString 76007 * @return {string} string representation of the tree 76008 */ 76009 toString: function() { 76010 return "TreeView " + this.id; 76011 }, 76012 76013 /** 76014 * Count of nodes in tree 76015 * @method getNodeCount 76016 * @return {int} number of nodes in the tree 76017 */ 76018 getNodeCount: function() { 76019 return this.getRoot().getNodeCount(); 76020 }, 76021 76022 /** 76023 * Returns an object which could be used to rebuild the tree. 76024 * It can be passed to the tree constructor to reproduce the same tree. 76025 * It will return false if any node loads dynamically, regardless of whether it is loaded or not. 76026 * @method getTreeDefinition 76027 * @return {Object | false} definition of the tree or false if any node is defined as dynamic 76028 */ 76029 getTreeDefinition: function() { 76030 return this.getRoot().getNodeDefinition(); 76031 }, 76032 76033 /** 76034 * Abstract method that is executed when a node is expanded 76035 * @method onExpand 76036 * @param node {Node} the node that was expanded 76037 * @deprecated use treeobj.subscribe("expand") instead 76038 */ 76039 onExpand: function(node) { }, 76040 76041 /** 76042 * Abstract method that is executed when a node is collapsed. 76043 * @method onCollapse 76044 * @param node {Node} the node that was collapsed. 76045 * @deprecated use treeobj.subscribe("collapse") instead 76046 */ 76047 onCollapse: function(node) { }, 76048 76049 /** 76050 * Sets the value of a property for all loaded nodes in the tree. 76051 * @method setNodesProperty 76052 * @param name {string} Name of the property to be set 76053 * @param value {any} value to be set 76054 * @param refresh {boolean} if present and true, it does a refresh 76055 */ 76056 setNodesProperty: function(name, value, refresh) { 76057 this.root.setNodesProperty(name,value); 76058 if (refresh) { 76059 this.root.refresh(); 76060 } 76061 }, 76062 /** 76063 * Event listener to toggle node highlight. 76064 * Can be assigned as listener to clickEvent, dblClickEvent and enterKeyPressed. 76065 * It returns false to prevent the default action. 76066 * @method onEventToggleHighlight 76067 * @param oArgs {any} it takes the arguments of any of the events mentioned above 76068 * @return {false} Always cancels the default action for the event 76069 */ 76070 onEventToggleHighlight: function (oArgs) { 76071 var node; 76072 if ('node' in oArgs && oArgs.node instanceof Widget.Node) { 76073 node = oArgs.node; 76074 } else if (oArgs instanceof Widget.Node) { 76075 node = oArgs; 76076 } else { 76077 return false;
76078 } 76079 node.toggleHighlight(); 76080 return false; 76081 } 76082 76083 76084}; 76085 76086/* Backwards compatibility aliases */ 76087var PROT = TV.prototype; 76088 /** 76089 * Renders the tree boilerplate and visible nodes. 76090 * Alias for render 76091 * @method draw 76092 * @deprecated Use render instead 76093 */ 76094PROT.draw = PROT.render; 76095 76096/* end backwards compatibility aliases */ 76097 76098YAHOO.augment(TV, YAHOO.util.EventProvider); 76099 76100/** 76101 * Running count of all nodes created in all trees. This is 76102 * used to provide unique identifies for all nodes. Deleting 76103 * nodes does not change the nodeCount. 76104 * @property YAHOO.widget.TreeView.nodeCount 76105 * @type int 76106 * @static 76107 */ 76108TV.nodeCount = 0; 76109 76110/** 76111 * Global cache of tree instances 76112 * @property YAHOO.widget.TreeView.trees 76113 * @type Array 76114 * @static 76115 * @private 76116 */ 76117TV.trees = []; 76118 76119/** 76120 * Global method for getting a tree by its id. Used in the generated 76121 * tree html. 76122 * @method YAHOO.widget.TreeView.getTree 76123 * @param treeId {String} the id of the tree instance 76124 * @return {TreeView} the tree instance requested, null if not found. 76125 * @static 76126 */ 76127TV.getTree = function(treeId) { 76128 var t = TV.trees[treeId]; 76129 return (t) ? t : null; 76130}; 76131 76132 76133/** 76134 * Global method for getting a node by its id. Used in the generated 76135 * tree html. 76136 * @method YAHOO.widget.TreeView.getNode 76137 * @param treeId {String} the id of the tree instance 76138 * @param nodeIndex {String} the index of the node to return 76139 * @return {Node} the node instance requested, null if not found 76140 * @static 76141 */ 76142TV.getNode = function(treeId, nodeIndex) { 76143 var t = TV.getTree(treeId); 76144 return (t) ? t.getNodeByIndex(nodeIndex) : null; 76145}; 76146 76147 76148/** 76149 * Class name assigned to elements that have the focus 76150 * 76151 * @property TreeView.FOCUS_CLASS_NAME
76152 * @type String 76153 * @static 76154 * @final 76155 * @default "ygtvfocus" 76156 76157 */ 76158TV.FOCUS_CLASS_NAME = 'ygtvfocus'; 76159 76160 76161 76162})(); 76163(function () { 76164 var Dom = YAHOO.util.Dom, 76165 Lang = YAHOO.lang, 76166 Event = YAHOO.util.Event; 76167/** 76168 * The base class for all tree nodes. The node's presentation and behavior in 76169 * response to mouse events is handled in Node subclasses. 76170 * @namespace YAHOO.widget 76171 * @class Node 76172 * @uses YAHOO.util.EventProvider 76173 * @param oData {object} a string or object containing the data that will 76174 * be used to render this node, and any custom attributes that should be 76175 * stored with the node (which is available in noderef.data). 76176 * All values in oData will be used to set equally named properties in the node 76177 * as long as the node does have such properties, they are not undefined, private or functions, 76178 * the rest of the values will be stored in noderef.data 76179 * @param oParent {Node} this node's parent node 76180 * @param expanded {boolean} the initial expanded/collapsed state (deprecated, use oData.expanded) 76181 * @constructor 76182 */
76183YAHOO.widget.Node = function(oData, oParent, expanded) { 76184 if (oData) { this.init(oData, oParent, expanded); } 76185}; 76186 76187YAHOO.widget.Node.prototype = { 76188 76189 /** 76190 * The index for this instance obtained from global counter in YAHOO.widget.TreeView. 76191 * @property index 76192 * @type int 76193 */ 76194 index: 0, 76195 76196 /** 76197 * This node's child node collection. 76198 * @property children 76199 * @type Node[] 76200 */ 76201 children: null, 76202 76203 /** 76204 * Tree instance this node is part of 76205 * @property tree 76206 * @type TreeView 76207 */ 76208 tree: null, 76209 76210 /** 76211 * The data linked to this node. This can be any object or primitive 76212 * value, and the data can be used in getNodeHtml(). 76213 * @property data 76214 * @type object 76215 */ 76216 data: null, 76217 76218 /** 76219 * Parent node 76220 * @property parent 76221 * @type Node 76222 */ 76223 parent: null, 76224 76225 /** 76226 * The depth of this node. We start at -1 for the root node. 76227 * @property depth 76228 * @type int 76229 */ 76230 depth: -1, 76231 76232 /** 76233 * The node's expanded/collapsed state 76234 * @property expanded 76235 * @type boolean 76236 */ 76237 expanded: false, 76238 76239 /** 76240 * Can multiple children be expanded at once? 76241 * @property multiExpand 76242 * @type boolean 76243 */ 76244 multiExpand: true, 76245 76246 /** 76247 * Should we render children for a collapsed node? It is possible that the 76248 * implementer will want to render the hidden data... @todo verify that we 76249 * need this, and implement it if we do. 76250 * @property renderHidden 76251 * @type boolean 76252 */ 76253 renderHidden: false, 76254 76255 /** 76256 * This flag is set to true when the html is generated for this node's 76257 * children, and set to false when new children are added. 76258 * @property childrenRendered 76259 * @type boolean 76260 */ 76261 childrenRendered: false, 76262 76263 /** 76264 * Dynamically loaded nodes only fetch the data the first time they are 76265 * expanded. This flag is set to true once the data has been fetched. 76266 * @property dynamicLoadComplete 76267 * @type boolean 76268 */ 76269 dynamicLoadComplete: false, 76270 76271 /** 76272 * This node's previous sibling 76273 * @property previousSibling 76274 * @type Node 76275 */ 76276 previousSibling: null, 76277 76278 /** 76279 * This node's next sibling 76280 * @property nextSibling 76281 * @type Node 76282 */ 76283 nextSibling: null, 76284 76285 /** 76286 * We can set the node up to call an external method to get the child 76287 * data dynamically. 76288 * @property _dynLoad 76289 * @type boolean 76290 * @private 76291 */ 76292 _dynLoad: false, 76293 76294 /** 76295 * Function to execute when we need to get this node's child data. 76296 * @property dataLoader 76297 * @type function 76298 */ 76299 dataLoader: null, 76300 76301 /** 76302 * This is true for dynamically loading nodes while waiting for the 76303 * callback to return. 76304 * @property isLoading 76305 * @type boolean 76306 */ 76307 isLoading: false, 76308 76309 /** 76310 * The toggle/branch icon will not show if this is set to false. This 76311 * could be useful if the implementer wants to have the child contain 76312 * extra info about the parent, rather than an actual node. 76313 * @property hasIcon 76314 * @type boolean 76315 */ 76316 hasIcon: true, 76317 /** 76318 * Attribute used for setting custom style class for tree node. 76319 */ 76320 customCls: null, 76321 /** 76322 * Used to configure what happens when a dynamic load node is expanded 76323 * and we discover that it does not have children. By default, it is 76324 * treated as if it still could have children (plus/minus icon). Set 76325 * iconMode to have it display like a leaf node instead. 76326 * @property iconMode 76327 * @type int 76328 */ 76329 iconMode: 0, 76330 76331 /** 76332 * Specifies whether or not the content area of the node should be allowed 76333 * to wrap. 76334 * @property nowrap 76335 * @type boolean 76336 * @default false 76337 */ 76338 nowrap: false, 76339 76340 /** 76341 * If true, the node will alway be rendered as a leaf node. This can be 76342 * used to override the presentation when dynamically loading the entire 76343 * tree. Setting this to true also disables the dynamic load call for the 76344 * node. 76345 * @property isLeaf 76346 * @type boolean 76347 * @default false 76348 */ 76349 isLeaf: false, 76350 76351/** 76352 * The CSS class for the html content container. Defaults to ygtvhtml, but 76353 * can be overridden to provide a custom presentation for a specific node. 76354 * @property contentStyle 76355 * @type string 76356 */ 76357 contentStyle: "", 76358 76359 76360 /** 76361 * The generated id that will contain the data passed in by the implementer. 76362 * @property contentElId 76363 * @type string 76364 */ 76365 contentElId: null, 76366 76367/** 76368 * Enables node highlighting. If true, the node can be highlighted and/
76368or propagate highlighting 76369 * @property enableHighlight 76370 * @type boolean 76371 * @default true 76372 */ 76373 enableHighlight: true, 76374 76375/** 76376 * Stores the highlight state. Can be any of: 76377 * <ul> 76378 * <li>0 - not highlighted</li> 76379 * <li>1 - highlighted</li> 76380 * <li>2 - some children highlighted</li> 76381 * </ul> 76382 * @property highlightState 76383 * @type integer 76384 * @default 0 76385 */ 76386 76387 highlightState: 0, 76388 76389 /** 76390 * Tells whether highlighting will be propagated up to the parents of the clicked node 76391 * @property propagateHighlightUp 76392 * @type boolean 76393 * @default false 76394 */ 76395 76396 propagateHighlightUp: false, 76397 76398 /** 76399 * Tells whether highlighting will be propagated down to the children of the clicked node 76400 * @property propagateHighlightDown 76401 * @type boolean 76402 * @default false 76403 */ 76404 76405 propagateHighlightDown: false, 76406 76407 /** 76408 * User-defined className to be added to the Node 76409 * @property className 76410 * @type string 76411 * @default null 76412 */ 76413 76414 className: null, 76415 76416 /** 76417 * The node type 76418 * @property _type 76419 * @private 76420 * @type string 76421 * @default "Node" 76422*/ 76423 _type: "Node", 76424 76425 /* 76426 spacerPath: "http://l.yimg.com/a/i/space.gif", 76427 expandedText: "Expanded", 76428 collapsedText: "Collapsed", 76429 loadingText: "Loading", 76430 */ 76431 76432 /** 76433 * Initializes this node, gets some of the properties from the parent 76434 * @method init 76435 * @param oData {object} a string or object containing the data that will 76436 * be used to render this node 76437 * @param oParent {Node} this node's parent node 76438 * @param expanded {boolean} the initial expanded/collapsed state 76439 */
76440 init: function(oData, oParent, expanded) { 76441 76442 this.data = {}; 76443 this.children = []; 76444 this.index = YAHOO.widget.TreeView.nodeCount; 76445 ++YAHOO.widget.TreeView.nodeCount; 76446 this.contentElId = "ygtvcontentel" + this.index; 76447 76448 if (Lang.isObject(oData)) { 76449 for (var property in oData) { 76450 if (oData.hasOwnProperty(property)) { 76451 if (property.charAt(0) != '_' && !Lang.isUndefined(this[property]) && !Lang.isFunction(this[property]) ) { 76452 this[property] = oData[property]; 76453 } else { 76454 this.data[property] = oData[property]; 76455 } 76456 } 76457 } 76458 } 76459 if (!Lang.isUndefined(expanded) ) { this.expanded = expanded; } 76460 76461 this.logger = new YAHOO.widget.LogWriter(this.toString()); 76462 76463 /** 76464 * The parentChange event is fired when a parent element is applied 76465 * to the node. This is useful if you need to apply tree-level 76466 * properties to a tree that need to happen if a node is moved from 76467 * one tree to another. 76468 * 76469 * @event parentChange 76470 * @type CustomEvent 76471 */ 76472 this.createEvent("parentChange", this); 76473 76474 // oParent should never be null except when we create the root node. 76475 if (oParent) { 76476 oParent.appendChild(this); 76477 } 76478 }, 76479 76480 /** 76481 * Certain properties for the node cannot be set until the parent 76482 * is known. This is called after the node is inserted into a tree. 76483 * the parent is also applied to this node's children in order to 76484 * make it possible to move a branch from one tree to another. 76485 * @method applyParent 76486 * @param {Node} parentNode this node's parent node 76487 * @return {boolean} true if the application was successful 76488 */ 76489 applyParent: function(parentNode) { 76490 if (!parentNode) { 76491 return false; 76492 } 76493 76494 this.tree = parentNode.tree; 76495 this.parent = parentNode; 76496 this.depth = parentNode.depth + 1; 76497 76498 // @todo why was this put here. This causes new nodes added at the 76499 // root level to lose the menu behavior. 76500 // if (! this.multiExpand) { 76501 // this.multiExpand = parentNode.multiExpand; 76502 // } 76503 76504 this.tree.regNode(this); 76505 parentNode.childrenRendered = false; 76506 76507 // cascade update existing children 76508 for (var i=0, len=this.children.length;i<len;++i) { 76509 this.children[i].applyParent(this); 76510 } 76511 76512 this.fireEvent("parentChange"); 76513 76514 return true; 76515 }, 76516 76517 /** 76518 * Appends a node to the child collection. 76519 * @method appendChild 76520 * @param childNode {Node} the new node 76521 * @return {Node} the child node 76522 * @private 76523 */ 76524 appendChild: function(childNode) { 76525 if (this.hasChildren()) { 76526 var sib = this.children[this.children.length - 1]; 76527 sib.nextSibling = childNode; 76528 childNode.previousSibling = sib; 76529 } 76530 this.children[this.children.length] = childNode; 76531 childNode.applyParent(this); 76532 76533 // part of the IE display issue workaround. If child nodes 76534 // are added after the initial render, and the node was 76535 // instantiated with expanded = true, we need to show the 76536 // children div now that the node has a child. 76537 if (this.childrenRendered && this.expanded) { 76538 this.getChildrenEl().style.display = ""; 76539 } 76540 76541 return childNode; 76542 }, 76543 76544 /** 76545 * Appends this node to the supplied node's child collection 76546 * @method appendTo 76547 * @param parentNode {Node} the node to append to. 76548 * @return {Node} The appended node 76549 */ 76550 appendTo: function(parentNode) { 76551 return parentNode.appendChild(this); 76552 }, 76553 76554 /** 76555 * Inserts this node before this supplied node 76556 * @method insertBefore 76557 * @param node {Node} the node to insert this node before 76558 * @return {Node} the inserted node 76559 */ 76560 insertBefore: function(node) { 76561 this.logger.log("insertBefore: " + node); 76562 var p = node.parent; 76563 if (p) { 76564 76565 if (this.tree) { 76566 this.tree.popNode(this); 76567 } 76568 76569 var refIndex = node.isChildOf(p); 76570 //this.logger.log(refIndex); 76571 p.children.splice(refIndex, 0, this); 76572 if (node.previousSibling) { 76573 node.previousSibling.nextSibling = this; 76574 } 76575 this.previousSibling = node.previousSibling; 76576 this.nextSibling = node; 76577 node.previousSibling = this; 76578 76579 this.applyParent(p); 76580 } 76581 76582 return this; 76583 }, 76584 76585 /**
76586 * Inserts this node after the supplied node 76587 * @method insertAfter 76588 * @param node {Node} the node to insert after 76589 * @return {Node} the inserted node 76590 */ 76591 insertAfter: function(node) { 76592 this.logger.log("insertAfter: " + node); 76593 var p = node.parent; 76594 if (p) { 76595 76596 if (this.tree) { 76597 this.tree.popNode(this); 76598 } 76599 76600 var refIndex = node.isChildOf(p); 76601 this.logger.log(refIndex); 76602 76603 if (!node.nextSibling) { 76604 this.nextSibling = null; 76605 return this.appendTo(p); 76606 } 76607 76608 p.children.splice(refIndex + 1, 0, this); 76609 76610 node.nextSibling.previousSibling = this; 76611 this.previousSibling = node; 76612 this.nextSibling = node.nextSibling; 76613 node.nextSibling = this; 76614 76615 this.applyParent(p); 76616 } 76617 76618 return this; 76619 }, 76620 76621 /** 76622 * Returns true if the Node is a child of supplied Node 76623 * @method isChildOf 76624 * @param parentNode {Node} the Node to check 76625 * @return {boolean} The node index if this Node is a child of 76626 * supplied Node, else -1. 76627 * @private 76628 */ 76629 isChildOf: function(parentNode) { 76630 if (parentNode && parentNode.children) { 76631 for (var i=0, len=parentNode.children.length; i<len ; ++i) { 76632 if (parentNode.children[i] === this) { 76633 return i; 76634 } 76635 } 76636 } 76637 76638 return -1; 76639 }, 76640 76641 /** 76642 * Returns a node array of this node's siblings, null if none. 76643 * @method getSiblings 76644 * @return Node[] 76645 */ 76646 getSiblings: function() { 76647 var sib = this.parent.children.slice(0); 76648 for (var i=0;i < sib.length && sib[i] != this;i++) {} 76649 sib.splice(i,1); 76650 if (sib.length) { return sib; } 76651 return null; 76652 }, 76653 76654 /** 76655 * Shows this node's children 76656 * @method showChildren 76657 */ 76658 showChildren: function() { 76659 if (!this.tree.animateExpand(this.getChildrenEl(), this)) { 76660 if (this.hasChildren()) { 76661 this.getChildrenEl().style.display = ""; 76662 } 76663 } 76664 }, 76665 76666 /** 76667 * Hides this node's children 76668 * @method hideChildren 76669 */ 76670 hideChildren: function() { 76671 this.logger.log("hiding " + this.index); 76672 76673 if (!this.tree.animateCollapse(this.getChildrenEl(), this)) { 76674 this.getChildrenEl().style.display = "none"; 76675 } 76676 }, 76677 76678 /** 76679 * Returns the id for this node's container div 76680 * @method getElId 76681 * @return {string} the element id 76682 */ 76683 getElId: function() { 76684 return "ygtv" + this.index; 76685 }, 76686 76687 /** 76688 * Returns the id for this node's children div 76689 * @method getChildrenElId 76690 * @return {string} the element id for this node's children div 76691 */ 76692 getChildrenElId: function() { 76693 return "ygtvc" + this.index; 76694 }, 76695 76696 /** 76697 * Returns the id for this node's toggle element 76698 * @method getToggleElId 76699 * @return {string} the toggel element id 76700 */ 76701 getToggleElId: function() { 76702 return "ygtvt" + this.index; 76703 }, 76704 76705 76706 /* 76707 * Returns the id for this node's spacer image. The spacer is positioned 76708 * over the toggle and provides feedback for screen readers. 76709 * @method getSpacerId 76710 * @return {string} the id for the spacer image 76711 */ 76712 /* 76713 getSpacerId: function() { 76714 return "ygtvspacer" + this.index; 76715 }, 76716 */ 76717 76718 /** 76719 * Returns this node's container html element 76720 * @method getEl 76721 * @return {HTMLElement} the container html element 76722 */ 76723 getEl: function() { 76724 return Dom.get(this.getElId()); 76725 }, 76726 76727 /** 76728 * Returns the div that was generated for this node's children 76729 * @method getChildrenEl 76730 * @return {HTMLElement} this node's children div 76731 */ 76732 getChildrenEl: function() { 76733 return Dom.get(this.getChildrenElId()); 76734 }, 76735 76736 /** 76737 * Returns the element that is being used for this node's toggle. 76738 * @method getToggleEl 76739 * @return {HTMLElement} this node's toggle html element 76740 */ 76741 getToggleEl: function() { 76742 return Dom.get(this.getToggleElId()); 76743 }, 76744 /** 76745 * Returns the outer html element for this node's content 76746 * @method getContentEl 76747 * @return {HTMLElement} the element 76748 */ 76749 getContentEl: function() { 76750 return Dom.get(this.contentElId); 76751 }, 76752 76753 76754 /* 76755 * Returns the element that is being used for this node's spacer. 76756 * @method getSpacer 76757 * @return {HTMLElement} this node's spacer html element 76758 */ 76759 /* 76760 getSpacer: function() {
76761 return document.getElementById( this.getSpacerId() ) || {}; 76762 }, 76763 */ 76764 76765 /* 76766 getStateText: function() { 76767 if (this.isLoading) { 76768 return this.loadingText; 76769 } else if (this.hasChildren(true)) { 76770 if (this.expanded) { 76771 return this.expandedText; 76772 } else { 76773 return this.collapsedText; 76774 } 76775 } else { 76776 return ""; 76777 } 76778 }, 76779 */ 76780 76781 /** 76782 * Hides this nodes children (creating them if necessary), changes the toggle style. 76783 * @method collapse 76784 */ 76785 collapse: function() { 76786 // Only collapse if currently expanded 76787 if (!this.expanded) { return; } 76788 76789 // fire the collapse event handler 76790 var ret = this.tree.onCollapse(this); 76791 76792 if (false === ret) { 76793 this.logger.log("Collapse was stopped by the abstract onCollapse"); 76794 return; 76795 } 76796 76797 ret = this.tree.fireEvent("collapse", this); 76798 76799 if (false === ret) { 76800 this.logger.log("Collapse was stopped by a custom event handler"); 76801 return; 76802 } 76803 76804 76805 if (!this.getEl()) { 76806 this.expanded = false; 76807 } else { 76808 // hide the child div 76809 this.hideChildren(); 76810 this.expanded = false; 76811 76812 this.updateIcon(); 76813 } 76814 76815 // this.getSpacer().title = this.getStateText(); 76816 76817 ret = this.tree.fireEvent("collapseComplete", this); 76818 76819 }, 76820 76821 /** 76822 * Shows this nodes children (creating them if necessary), changes the 76823 * toggle style, and collapses its siblings if multiExpand is not set. 76824 * @method expand 76825 */ 76826 expand: function(lazySource) { 76827 // Only expand if currently collapsed. 76828 if (this.isLoading || (this.expanded && !lazySource)) { 76829 return; 76830 } 76831 76832 var ret = true; 76833 76834 // When returning from the lazy load handler, expand is called again 76835 // in order to render the new children. The "expand" event already 76836 // fired before fething the new data, so we need to skip it now. 76837 if (!lazySource) { 76838 // fire the expand event handler 76839 ret = this.tree.onExpand(this); 76840 76841 if (false === ret) { 76842 this.logger.log("Expand was stopped by the abstract onExpand"); 76843 return; 76844 } 76845 76846 ret = this.tree.fireEvent("expand", this); 76847 } 76848 76849 if (false === ret) { 76850 this.logger.log("Expand was stopped by the custom event handler"); 76851 return; 76852 } 76853 76854 if (!this.getEl()) { 76855 this.expanded = true; 76856 return; 76857 } 76858 76859 if (!this.childrenRendered) { 76860 this.logger.log("children not rendered yet"); 76861 this.getChildrenEl().innerHTML = this.renderChildren(); 76862 } else { 76863 this.logger.log("children already rendered"); 76864 } 76865 76866 this.expanded = true; 76867 76868 this.updateIcon(); 76869 76870 // this.getSpacer().title = this.getStateText(); 76871 76872 // We do an extra check for children here because the lazy 76873 // load feature can expose nodes that have no children. 76874 76875 // if (!this.hasChildren()) { 76876 if (this.isLoading) { 76877 this.expanded = false; 76878 return; 76879 } 76880 76881 if (! this.multiExpand) { 76882 var sibs = this.getSiblings(); 76883 for (var i=0; sibs && i<sibs.length; ++i) { 76884 if (sibs[i] != this && sibs[i].expanded) { 76885 sibs[i].collapse(); 76886 } 76887 } 76888 } 76889 76890 this.showChildren(); 76891 76892 ret = this.tree.fireEvent("expandComplete", this); 76893 }, 76894 76895 updateIcon: function() { 76896 if (this.hasIcon) { 76897 var el = this.getToggleEl(); 76898 if (el) { 76899 // Alfresco Changes Start 76900 if (this._customRegEx(this.customCls) && this._customRegEx(this.customCls).test(el.className)) 76901 { 76902 el.className = el.className.replace(this._customRegEx(this.customCls), this.getStyle()); 76903 } 76904 else 76905 { 76906 el.className = el.className.replace(/\bygtv(([tl][pmn]h?)|(loading))\b/gi, this.get
76906Style()); 76907 } 76908 // Alfresco Changes End 76909 } 76910 } 76911 el = Dom.get('ygtvtableel' + this.index); 76912 if (el) { 76913 if (this.expanded) { 76914 Dom.replaceClass(el,'ygtv-collapsed','ygtv-expanded'); 76915 } else { 76916 Dom.replaceClass(el,'ygtv-expanded','ygtv-collapsed'); 76917 } 76918 } 76919 }, 76920 76921 /** 76922 * Returns the css style name for the toggle 76923 * @method getStyle 76924 * @return {string} the css class for this node's toggle 76925 */ 76926 getStyle : function() 76927 { 76928 // this.logger.log("No children, " + " isDyanmic: " + this.isDynamic() + " expanded: " + this.expanded); 76929 if (this.isLoading) 76930 { 76931 this.logger.log("returning the loading icon"); 76932 return "ygtvloading"; 76933 } 76934 else 76935 { 76936 // type p=plus(expand), m=minus(collapase), n=none(no children) 76937 var type = "n"; 76938 if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) 76939 { 76940 // if (this.hasChildren(true)) { 76941 type = (this.expanded) ? "m" : "p"; 76942 } 76943 // Alfresco Changes Start 76944 if (this.customCls) 76945 { 76946 return this.customCls+type; 76947 } 76948 else 76949 { 76950 // location top or bottom, middle nodes also get the top style 76951 var loc = (this.nextSibling) ? "t" : "l"; 76952 return "ygtv" + loc + type; 76953 } 76954 // Alfresco Changes End 76955 } 76956 }, 76957 76958 /** 76959 * Returns the hover style for the icon 76960 * @return {string} the css class hover state 76961 * @method getHoverStyle 76962 */ 76963 getHoverStyle: function() { 76964 var s = this.getStyle(); 76965 if (this.hasChildren(true) && !this.isLoading) { 76966 s += "h"; 76967 } 76968 return s; 76969 }, 76970 76971 /** 76972 * Recursively expands all of this node's children. 76973 * @method expandAll 76974 */ 76975 expandAll: function() { 76976 var l = this.children.length; 76977 for (var i=0;i<l;++i) { 76978 var c = this.children[i]; 76979 if (c.isDynamic()) { 76980 this.logger.log("Not supported (lazy load + expand all)"); 76981 break; 76982 } else if (! c.multiExpand) { 76983 this.logger.log("Not supported (no multi-expand + expand all)"); 76984 break; 76985 } else { 76986 c.expand();
76987 c.expandAll(); 76988 } 76989 } 76990 }, 76991 76992 /** 76993 * Recursively collapses all of this node's children. 76994 * @method collapseAll 76995 */ 76996 collapseAll: function() { 76997 for (var i=0;i<this.children.length;++i) { 76998 this.children[i].collapse(); 76999 this.children[i].collapseAll(); 77000 } 77001 }, 77002 77003 /** 77004 * Configures this node for dynamically obtaining the child data 77005 * when the node is first expanded. Calling it without the callback 77006 * will turn off dynamic load for the node. 77007 * @method setDynamicLoad 77008 * @param fmDataLoader {function} the function that will be used to get the data. 77009 * @param iconMode {int} configures the icon that is displayed when a dynamic 77010 * load node is expanded the first time without children. By default, the 77011 * "collapse" icon will be used. If set to 1, the leaf node icon will be 77012 * displayed. 77013 */ 77014 setDynamicLoad: function(fnDataLoader, iconMode) { 77015 if (fnDataLoader) { 77016 this.dataLoader = fnDataLoader; 77017 this._dynLoad = true; 77018 } else { 77019 this.dataLoader = null; 77020 this._dynLoad = false; 77021 } 77022 77023 if (iconMode) { 77024 this.iconMode = iconMode; 77025 } 77026 }, 77027 77028 /** 77029 * Evaluates if this node is the root node of the tree 77030 * @method isRoot 77031 * @return {boolean} true if this is the root node 77032 */ 77033 isRoot: function() { 77034 return (this == this.tree.root); 77035 }, 77036 77037 /** 77038 * Evaluates if this node's children should be loaded dynamically. Looks for 77039 * the property both in this instance and the root node. If the tree is 77040 * defined to load all children dynamically, the data callback function is 77041 * defined in the root node 77042 * @method isDynamic 77043 * @return {boolean} true if this node's children are to be loaded dynamically 77044 */ 77045 isDynamic: function() { 77046 if (this.isLeaf) { 77047 return false; 77048 } else { 77049 return (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad)); 77050 // this.logger.log("isDynamic: " + lazy); 77051 // return lazy; 77052 } 77053 }, 77054 77055 /** 77056 * Returns the current icon mode. This refers to the way childless dynamic 77057 * load nodes appear (this comes into play only after the initial dynamic 77058 * load request produced no children). 77059 * @method getIconMode 77060 * @return {int} 0 for collapse style, 1 for leaf node style 77061 */ 77062 getIconMode: function() { 77063 return (this.iconMode || this.tree.root.iconMode); 77064 }, 77065 77066 /** 77067 * Checks if this node has children. If this node is lazy-loading and the 77068 * children have not been rendered, we do not know whether or not there 77069 * are actual children. In most cases, we need to assume that there are 77070 * children (for instance, the toggle needs to show the expandable 77071 * presentation state). In other times we want to know if there are rendered 77072 * children. For the latter, "checkForLazyLoad" should be false. 77073 * @method hasChildren 77074 * @param checkForLazyLoad {boolean} should we check for unloaded children? 77075 * @return {boolean} true if this has children or if it might and we are 77076 * checking for this condition. 77077 */ 77078 hasChildren: function(checkForLazyLoad) { 77079 if (this.isLeaf) { 77080 return false; 77081 } else { 77082 return ( this.children.length > 0 || 77083 (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) 77084 ); 77085 } 77086 }, 77087 77088 /** 77089 * Expands if node is collapsed, collapses otherwise. 77090 * @method toggle 77091 */ 77092 toggle: function() { 77093 if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) { 77094 if (this.expanded) { this.collapse(); } else { this.expand(); } 77095 } 77096 }, 77097 77098 /** 77099 * Returns the markup for this node and its children. 77100 * @method getHtml 77101 * @return {string} the markup for this node and its expanded children. 77102 */ 77103 getHtml: function() { 77104 77105 this.childrenRendered = false; 77106 77107 return ['<div class="ygtvitem" id="' , this.getElId() , '">' ,this.getNodeHtml() , this.getChildrenHtml() ,'</div>'].join(""); 77108 }, 77109 77110 /** 77111 * Called when first rendering the tree. We always build the div that will 77112 * contain this nodes children, but we don't render the children themselves 77113 * unless this node is expanded. 77114 * @method getChildrenHtml 77115 * @return {string} the children container div html and any expanded children 77116 * @private 77117 */ 77118 getChildrenHtml: function() { 77119 77120 77121 var sb = []; 77122 sb[sb.length] = '<div class="ygtvchildren" id="' + this.getChildrenElId() + '"'; 77123 77124 // This is a workaround for an IE rendering issue, the child div has layout 77125 // in IE, creating extra space if a leaf node is created with the exp
77125anded 77126 // property set to true. 77127 if (!this.expanded || !this.hasChildren()) { 77128 sb[sb.length] = ' style="display:none;"'; 77129 } 77130 sb[sb.length] = '>'; 77131 77132 // this.logger.log(["index", this.index, 77133 // "hasChildren", this.hasChildren(true), 77134 // "expanded", this.expanded, 77135 // "renderHidden", this.renderHidden, 77136 // "isDynamic", this.isDynamic()]); 77137 77138 // Don't render the actual child node HTML unless this node is expanded. 77139 if ( (this.hasChildren(true) && this.expanded) || 77140 (this.renderHidden && !this.isDynamic()) ) { 77141 sb[sb.length] = this.renderChildren(); 77142 } 77143 77144 sb[sb.length] = '</div>'; 77145 77146 return sb.join(""); 77147 }, 77148 77149 /** 77150 * Generates the markup for the child nodes. This is not done until the node 77151 * is expanded. 77152 * @method renderChildren 77153 * @return {string} the html for this node's children 77154 * @private 77155 */ 77156 renderChildren: function() { 77157 77158 this.logger.log("rendering children for " + this.index); 77159 77160 var node = this; 77161 77162 if (this.isDynamic() && !this.dynamicLoadComplete) { 77163 this.isLoading = true; 77164 this.tree.locked = true; 77165 77166 if (this.dataLoader) { 77167 this.logger.log("Using dynamic loader defined for this node"); 77168 77169 setTimeout( 77170 function() { 77171 node.dataLoader(node, 77172 function() { 77173 node.loadComplete(); 77174 }); 77175 }, 10); 77176 77177 } else if (this.tree.root.dataLoader) { 77178 this.logger.log("Using the tree-level dynamic loader"); 77179 77180 setTimeout( 77181 function() { 77182 node.tree.root.dataLoader(node, 77183 function() { 77184 node.loadComplete(); 77185 }); 77186 }, 10); 77187 77188 } else { 77189 this.logger.log("no loader found"); 77190 return "Error: data loader not found or not specified."; 77191 } 77192 77193 return ""; 77194 77195 } else { 77196 return this.completeRender(); 77197 } 77198 }, 77199 77200 /** 77201 * Called when we know we have all the child data. 77202 * @method completeRender 77203 * @return {string} children html 77204 */ 77205 completeRender: function() { 77206 this.logger.log("completeRender: " + this.index + ", # of children: " + this.children.length); 77207 var sb = []; 77208 77209 for (var i=0; i < this.children.length; ++i) { 77210 // this.children[i].childrenRendered = false; 77211 sb[sb.length] = this.children[i].getHtml(); 77212 } 77213 77214 this.childrenRendered = true; 77215 77216 return sb.join(""); 77217 }, 77218 77219 /** 77220 * Load complete is the callback function we pass to the data provider 77221 * in dynamic load situations. 77222 * @method loadComplete 77223 */ 77224 loadComplete: function() { 77225 this.logger.log(this.index + " loadComplete, children: " + this.children.length); 77226 this.getChildrenEl().innerHTML = this.completeRender(); 77227 if (this.propagateHighlightDown) { 77228 if (this.highlightState === 1 && !this.tree.singleNodeHighlight) { 77229 for (var i = 0; i < this.children.length; i++) { 77230 this.children[i].highlight(true); 77231 } 77232 } else if (this.highlightState === 0 || this.tree.singleNodeHighlight) { 77233 for (i = 0; i < this.children.length; i++) { 77234 this.children[i].unhighlight(true); 77235 } 77236 } // if (highlighState == 2) leave child nodes with whichever highlight state they are set 77237 } 77238 77239 this.dynamicLoadComplete = true; 77240 this.isLoading = false; 77241 this.expand(true); 77242 this.tree.locked = false; 77243 }, 77244 77245 /** 77246 * Returns this node's ancestor at the specified depth. 77247 * @method getAncestor 77248 * @param {int} depth the depth of the ancestor. 77249 * @return {Node} the ancestor 77250 */ 77251 getAncestor: function(depth) { 77252 if (depth >= this.depth || depth < 0) { 77253 this.logger.log("illegal getAncestor depth: " + depth); 77254 return null; 77255 } 77256 77257 var p = this.parent; 77258 77259 while (p.depth > depth) { 77260 p = p.parent; 77261 } 77262 77263 return p; 77264 }, 77265 77266 /** 77267 * Returns the css class for the spacer at the specified depth for 77268 * this node. If this node's ancestor at the specified depth 77269 * has a next sibling the presentation is different than if it 77270 * does not have a next sibling 77271 * @method getDepthStyle 77272 * @param {int} depth the depth of the ancestor. 77273 * @return {string} the css class for the spacer 77274 */ 77275 getDepthStyle: function(depth) { 77276 return (this.getAncestor(depth).nextSibling) ? 77277 "ygtvdepthcell" : "ygtvblankdepthcell"; 77278 }, 77279 77280 /** 77281 * Get the markup for the node. This may be overrided so that we can 77282 * support different types of nodes. 77283 * @method getNodeHtml 77284 * @return {string} The HTML that will render this node. 77285 */ 77286 getNodeHtml: function() { 77287 this.logger.log("Generating html"); 77288 var sb = []; 77289 77290 sb[sb.length] = '<table id="ygtvtableel' + this.index + '" border="0" cellpadding="0" cellspacing="0" class="ygtvtable ygtvdepth' + this.depth; 77291 sb[sb.length] = ' ygtv-' + (this.expanded?'expanded':'collapsed'); 77292 if (this.enableHighlight) { 77293 sb[sb.length] = ' ygtv-highlight' + this.highlightState; 77294 } 77295 if (this.className) { 77296 sb[sb.length] = ' ' + this.className; 77297 } 77298 sb[sb.length] = '"><tr class="ygtvrow">'; 77299 77300 for (var i=0;i<this.depth;++i) { 77301 sb[sb.length] = '<td class="ygtvcell ' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>'; 77302 } 77303 77304 if (this.hasIcon) { 77305 sb[sb.length] = '<td id="' + this.getToggleElId(); 77306 sb[sb.length] = '" class="ygtvcell ';
77307 sb[sb.length] = this.getStyle() ; 77308 sb[sb.length] = '"><a href="#" class="ygtvspacer"> </a></td>'; 77309 } 77310 77311 sb[sb.length] = '<td id="' + this.contentElId; 77312 sb[sb.length] = '" class="ygtvcell '; 77313 sb[sb.length] = this.contentStyle + ' ygtvcontent" '; 77314 sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : ''; 77315 sb[sb.length] = ' >'; 77316 sb[sb.length] = this.getContentHtml(); 77317 sb[sb.length] = '</td></tr></table>'; 77318 77319 return sb.join(""); 77320 77321 }, 77322 /** 77323 * Get the markup for the contents of the node. This is designed to be overrided so that we can 77324 * support different types of nodes. 77325 * @method getContentHtml 77326 * @return {string} The HTML that will render the content of this node. 77327 */ 77328 getContentHtml: function () { 77329 return ""; 77330 }, 77331 77332 /** 77333 * Regenerates the html for this node and its children. To be used when the 77334 * node is expanded and new children have been added. 77335 * @method refresh 77336 */ 77337 refresh: function() { 77338 // this.loadComplete(); 77339 this.getChildrenEl().innerHTML = this.completeRender(); 77340 77341 if (this.hasIcon) { 77342 var el = this.getToggleEl(); 77343 if (el) { 77344 el.className = el.className.replace(/\bygtv[lt][nmp]h*\b/gi,this.getStyle()); 77345 } 77346 } 77347 }, 77348 77349 /** 77350 * Node toString 77351 * @method toString 77352 * @return {string} string representation of the node 77353 */ 77354 toString: function() { 77355 return this._type + " (" + this.index + ")"; 77356 }, 77357 /** 77358 * array of items that had the focus set on them 77359 * so that they can be cleaned when focus is lost 77360 * @property _focusHighlightedItems 77361 * @type Array of DOM elements 77362 * @private 77363 */ 77364 _focusHighlightedItems: [], 77365 /** 77366 * DOM element that actually got the browser focus 77367 * @property _focusedItem 77368 * @type DOM element 77369 * @private 77370 */ 77371 _focusedItem: null, 77372 77373 /** 77374 * Returns true if there are any elements in the node that can 77375 * accept the real actual browser focus 77376 * @method _canHaveFocus 77377 * @return {boolean} success 77378 * @private 77379 */ 77380 _canHaveFocus: function() { 77381 return this.getEl().getElementsByTagName('a').length > 0; 77382 }, 77383 /** 77384 * Removes the focus of previously selected Node 77385 * @method _removeFocus 77386 * @private 77387 */ 77388 _removeFocus:function () { 77389 if (this._focusedItem) { 77390 Event.removeListener(this._focusedItem,'blur'); 77391 this._focusedItem = null; 77392 } 77393 var el; 77394 while ((el = this._focusHighlightedItems.shift())) { // yes, it is meant as an assignment, really 77395 Dom.removeClass(el,YAHOO.widget.TreeView.FOCUS_CLASS_NAME ); 77396 } 77397 }, 77398 /** 77399 * Sets the focus on the node element. 77400 * It will only be able to set the focus on nodes that have anchor elements in it. 77401 * Toggle or branch icons have anchors and can be focused on. 77402 * If will fail in nodes that have no anchor 77403 * @method focus 77404 * @return {boolean} success 77405 */ 77406 focus: function () { 77407 var focused = false, self = this; 77408 77409 if (this.tree.currentFocus) { 77410 this.tree.currentFocus._removeFocus(); 77411 } 77412 77413 var expandParent = function (node) { 77414 if (node.parent) { 77415 expandParent(node.parent); 77416 node.parent.expand(); 77417 } 77418 }; 77419 expandParent(this); 77420 77421 Dom.getElementsBy ( 77422 function (el) { 77423 return (/ygtv(([tl][pmn]h?)|(content))/).test(el.className); 77424 } , 77425 'td' , 77426 self.getEl().firstChild , 77427 function (el) { 77428 Dom.addClass(el, YAHOO.widget.TreeView.FOCUS_CLASS_NAME ); 77429 if (!focused) { 77430 var aEl = el.getElementsByTagName('a'); 77431 if (aEl.length) { 77432 aEl = aEl[0]; 77433 aEl.focus(); 77434 self._focusedItem = aEl; 77435 Event.on(aEl,'blur',function () { 77436 self.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null}); 77437 self.tree.currentFocus = null; 77438 self._removeFocus(); 77439 }); 77440 focused = true; 77441 } 77442 } 77443 self._focusHighlightedItems.push(el); 77444 } 77445 ); 77446 if (focused) { 77447 this.tree.fireEvent('focusChanged',{oldNode:this.tree.currentFocus,newNode:this}); 77448 this.tree.currentFocus = this; 77449 } else { 77450 this.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null}); 77451 this.tree.currentFocus = null; 77452 this._removeFocus(); 77453 } 77454 return focused; 77455 }, 77456 77457 /** 77458 * Count of nodes in a branch 77459 * @method getNodeCount 77460 * @return {int} number of nodes in the branch 77461 */ 77462 getNodeCount: function() { 77463 for (var i = 0, count = 0;i< this.children.length;i++) { 77464 count += this.children[i].getNodeCount(); 77465 } 77466 return count + 1; 77467 }, 77468 77469 /** 77470 * Returns an object which could be used to build a tree out of this node and its children. 77471 * It can be passed to the tree constructor to reproduce this node as a tree. 77472 * It will return false if the node or any children loads dynamically, regardless of whether it is loaded or not. 77473 * @method getNodeDefinition 77474 * @return {Object | false} definition of the tree or false if the node or any children is defined as dynamic 77475 */ 77476 getNodeDefinition: function() { 77477 77478 if (this.isDynamic()) { return false; } 77479 77480 var def, defs = Lang.merge(this.data), children = []; 77481 77482 77483 77484 if (this.expanded) {defs.expanded = this.expanded; } 77485 if (!this.multiExpand) { defs.multiExpand = this.multiExpand; } 77486 if (this.renderHidden) { defs.renderHidden = this.renderHidden; } 77487 if (!this.hasIcon) { defs.hasIcon = this.hasIcon; } 77488 if (this.nowrap) { defs.nowrap = this.nowrap; } 77489 if (this.className) { defs.className = this.className; } 77490 if (this.editable) { defs.editable = this.editable; } 77491 if (!this.enableHighlight) { defs.enableHighlight = this.enableHighlight; } 77492 if (this.highlightState) { defs.highlightState = this.highlightState; }
77493 if (this.propagateHighlightUp) { defs.propagateHighlightUp = this.propagateHighlightUp; } 77494 if (this.propagateHighlightDown) { defs.propagateHighlightDown = this.propagateHighlightDown; } 77495 defs.type = this._type; 77496 77497 77498 77499 for (var i = 0; i < this.children.length;i++) { 77500 def = this.children[i].getNodeDefinition(); 77501 if (def === false) { return false;} 77502 children.push(def); 77503 } 77504 if (children.length) { defs.children = children; } 77505 return defs; 77506 }, 77507 77508 77509 /** 77510 * Generates the link that will invoke this node's toggle method 77511 * @method getToggleLink 77512 * @return {string} the javascript url for toggling this node 77513 */ 77514 getToggleLink: function() { 77515 return 'return false;'; 77516 }, 77517 77518 /** 77519 * Sets the value of property for this node and all loaded descendants. 77520 * Only public and defined properties can be set, not methods. 77521 * Values for unknown properties will be assigned to the refNode.data object 77522 * @method setNodesProperty 77523 * @param name {string} Name of the property to be set 77524 * @param value {any} value to be set 77525 * @param refresh {boolean} if present and true, it does a refresh 77526 */ 77527 setNodesProperty: function(name, value, refresh) { 77528 if (name.charAt(0) != '_' && !Lang.isUndefined(this[name]) && !Lang.isFunction(this[name]) ) { 77529 this[name] = value; 77530 } else { 77531 this.data[name] = value; 77532 } 77533 for (var i = 0; i < this.children.length;i++) { 77534 this.children[i].setNodesProperty(name,value); 77535 } 77536 if (refresh) { 77537 this.refresh(); 77538 } 77539 }, 77540 /** 77541 * Toggles the highlighted state of a Node 77542 * @method toggleHighlight 77543 */ 77544 toggleHighlight: function() { 77545 if (this.enableHighlight) { 77546 // unhighlights only if fully highligthed. For not or partially highlighted it will highlight 77547 if (this.highlightState == 1) { 77548 this.unhighlight(); 77549 } else { 77550 this.highlight(); 77551 } 77552 } 77553 }, 77554 77555 /** 77556 * Turns highlighting on node. 77557 * @method highlight 77558 * @param _silent {boolean} optional, don't fire the highlightEvent 77559 */ 77560 highlight: function(_silent) { 77561 if (this.enableHighlight) { 77562 if (this.tree.singleNodeHighlight) { 77563 if (this.tree._currentlyHighlighted) { 77564 this.tree._currentlyHighlighted.unhighlight(_silent); 77565 } 77566 this.tree._currentlyHighlighted = this; 77567 } 77568 this.highlightState = 1; 77569 this._setHighlightClassName(); 77570 if (!this.tree.singleNodeHighlight) { 77571 if (this.propagateHighlightDown) { 77572 for (var i = 0;i < this.children.length;i++) { 77573 this.children[i].highlight(true); 77574 } 77575 } 77576 if (this.propagateHighlightUp) { 77577 if (this.parent) { 77578 this.parent._childrenHighlighted(); 77579 } 77580 } 77581 } 77582 if (!_silent) { 77583 this.tree.fireEvent('highlightEvent',this); 77584 } 77585 } 77586 }, 77587 /** 77588 * Turns highlighting off a node. 77589 * @method unhighlight 77590 * @param _silent {boolean} optional, don't fire the highlightEvent 77591 */ 77592 unhighlight: function(_silent) { 77593 if (this.enableHighlight) { 77594 // might have checked singleNodeHighlight but it wouldn't really matter either way 77595 this.tree._currentlyHighlighted = null; 77596 this.highlightState = 0; 77597 this._setHighlightClassName(); 77598 if (!this.tree.singleNodeHighlight) { 77599 if (this.propagateHighlightDown) { 77600 for (var i = 0;i < this.children.length;i++) { 77601 this.children[i].unhighlight(true); 77602 } 77603 } 77604 if (this.propagateHighlightUp) { 77605 if (this.parent) { 77606 this.parent._childrenHighlighted(); 77607 } 77608 } 77609 } 77610 if (!_silent) { 77611 this.tree.fireEvent('highlightEvent',this); 77612 } 77613 } 77614 }, 77615 /** 77616 * Checks whether all or part of the children of a node are highlighted and 77617 * sets the node highlight to full, none or partial highlight. 77618 * If set to propagate it will further call the parent
77619 * @method _childrenHighlighted 77620 * @private 77621 */ 77622 _childrenHighlighted: function() { 77623 var yes = false, no = false; 77624 if (this.enableHighlight) { 77625 for (var i = 0;i < this.children.length;i++) { 77626 switch(this.children[i].highlightState) { 77627 case 0: 77628 no = true; 77629 break; 77630 case 1: 77631 yes = true; 77632 break; 77633 case 2: 77634 yes = no = true; 77635 break; 77636 } 77637 } 77638 if (yes && no) { 77639 this.highlightState = 2; 77640 } else if (yes) { 77641 this.highlightState = 1; 77642 } else { 77643 this.highlightState = 0; 77644 } 77645 this._setHighlightClassName(); 77646 if (this.propagateHighlightUp) { 77647 if (this.parent) { 77648 this.parent._childrenHighlighted(); 77649 } 77650 } 77651 } 77652 }, 77653 77654 /** 77655 * Changes the classNames on the toggle and content containers to reflect the current highlighting 77656 * @method _setHighlightClassName 77657 * @private 77658 */ 77659 _setHighlightClassName: function() { 77660 var el = Dom.get('ygtvtableel' + this.index); 77661 if (el) { 77662 el.className = el.className.replace(/\bygtv-highlight\d\b/gi,'ygtv-highlight' + this.highlightState); 77663 } 77664 }, 77665 77666 //Start Changes 77667 /** 77668 * Gets a RegExp based on custom style. This is used for expanding collapsing nodes that have custom style. 77669 * @param customCls 77670 * @returns {RegExp} 77671 */ 77672 _customRegEx : function(customCls) 77673 { 77674 var customRegEx = null; 77675 if (customCls != null) 77676 { 77677 customRegEx = new RegExp(customCls+"[mpn]", "g"); 77678 } 77679 return customRegEx; 77680 } 77681 //End Changes 77682 77683}; 77684 77685YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider); 77686})(); 77687/** 77688 * A custom YAHOO.widget.Node that handles the unique nature of 77689 * the virtual, presentationless root node. 77690 * @namespace YAHOO.widget 77691 * @class RootNode 77692 * @extends YAHOO.widget.Node 77693 * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to 77694 * @constructor 77695 */ 77696YAHOO.widget.RootNode = function(oTree) { 77697 // Initialize the node with null params. The root node is a 77698 // special case where the node has no presentation. So we have 77699 // to alter the standard properties a bit. 77700 this.init(null, null, true); 77701 77702 /* 77703 * For the root node, we get the tree reference from as a param 77704 * to the constructor instead of from the parent element. 77705 */ 77706 this.tree = oTree; 77707}; 77708 77709YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, { 77710 77711 /** 77712 * The node type 77713 * @property _type 77714 * @type string 77715 * @private 77716 * @default "RootNode" 77717 */ 77718 _type: "RootNode", 77719 77720 // overrides YAHOO.widget.Node 77721 getNodeHtml: function() { 77722 return ""; 77723 }, 77724 77725 toString: function() { 77726 return this._type; 77727 }, 77728 77729 loadComplete: function() { 77730 this.tree.draw(); 77731 }, 77732 77733 /** 77734 * Count of nodes in tree. 77735 * It overrides Nodes.getNodeCount because the root node should not be counted. 77736 * @method getNodeCount 77737 * @return {int} number of nodes in the tree 77738 */ 77739 getNodeCount: function() { 77740 for (var i = 0, count = 0;i< this.children.length;i++) { 77741 count += this.children[i].getNodeCount(); 77742 } 77743 return count; 77744 }, 77745 77746 /** 77747 * Returns an object which could be used to build a tree out of this node and its children. 77748 * It can be passed to the tree constructor to reproduce this node as a tree. 77749 * Since the RootNode is automatically created by treeView, 77750 * its own definition is excluded from the returned node definition 77751 * which only contains its children. 77752 * @method getNodeDefinition 77753 * @return {Object | false} definition of the tree or false if any child node is defined as dynamic 77754 */ 77755 getNodeDefinition: function() { 77756 77757 for (var def, defs = [], i = 0; i < this.children.length;i++) { 77758 def = this.children[i].getNodeDefinition(); 77759 if (def === false) { return false;} 77760 defs.push(def); 77761 } 77762 return defs; 77763 }, 77764 77765 collapse: function() {}, 77766 expand: function() {}, 77767 getSiblings: function() { return null; }, 77768 focus: function () {} 77769 77770}); 77771(function () { 77772 var Dom = YAHOO.util.Dom, 77773 Lang = YAHOO.lang, 77774 Event = YAHOO.util.Event; 77775/** 77776 * The default node presentation. The first parameter should be 77777 * either a string that will be used as the node's label, or an object 77778 * that has at least a string property called label. By default, clicking the
77779 * label will toggle the expanded/collapsed state of the node. By 77780 * setting the href property of the instance, this behavior can be 77781 * changed so that the label will go to the specified href. 77782 * @namespace YAHOO.widget 77783 * @class TextNode 77784 * @extends YAHOO.widget.Node 77785 * @constructor 77786 * @param oData {object} a string or object containing the data that will 77787 * be used to render this node. 77788 * Providing a string is the same as providing an object with a single property named label. 77789 * All values in the oData will be used to set equally named properties in the node 77790 * as long as the node does have such properties, they are not undefined, private or functions. 77791 * All attributes are made available in noderef.data, which 77792 * can be used to store custom attributes. TreeView.getNode(s)ByProperty 77793 * can be used to retrieve a node by one of the attributes. 77794 * @param oParent {YAHOO.widget.Node} this node's parent node 77795 * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded) 77796 */ 77797YAHOO.widget.TextNode = function(o
77797Data, oParent, expanded) { 77798 77799 if (oData) { 77800 if (Lang.isString(oData)) { 77801 oData = { label: oData }; 77802 } 77803 this.init(oData, oParent, expanded); 77804 this.setUpLabel(oData); 77805 } 77806 77807 this.logger = new YAHOO.widget.LogWriter(this.toString()); 77808}; 77809 77810YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, { 77811 77812 /** 77813 * The CSS class for the label href. Defaults to ygtvlabel, but can be 77814 * overridden to provide a custom presentation for a specific node. 77815 * @property labelStyle 77816 * @type string 77817 */ 77818 labelStyle: "ygtvlabel", 77819 77820 /** 77821 * The derived element id of the label for this node 77822 * @property labelElId 77823 * @type string 77824 */ 77825 labelElId: null, 77826 77827 /** 77828 * The text for the label. It is assumed that the oData parameter will 77829 * either be a string that will be used as the label, or an object that 77830 * has a property called "label" that we will use. 77831 * @property label 77832 * @type string 77833 */ 77834 label: null, 77835 77836 /** 77837 * The text for the title (tooltip) for the label element 77838 * @property title 77839 * @type string 77840 */ 77841 title: null, 77842 77843 /** 77844 * The href for the node's label. If one is not specified, the href will 77845 * be set so that it toggles the node. 77846 * @property href 77847 * @type string 77848 */ 77849 href: null, 77850 77851 /** 77852 * The label href target, defaults to current window 77853 * @property target 77854 * @type string 77855 */ 77856 target: "_self", 77857 77858 /** 77859 * The node type 77860 * @property _type 77861 * @private 77862 * @type string 77863 * @default "TextNode" 77864 */ 77865 _type: "TextNode", 77866 77867 77868 /** 77869 * Sets up the node label 77870 * @method setUpLabel 77871 * @param oData string containing the label, or an object with a label property 77872 */
77873 setUpLabel: function(oData) { 77874 77875 if (Lang.isString(oData)) { 77876 oData = { 77877 label: oData 77878 }; 77879 } else { 77880 if (oData.style) { 77881 this.labelStyle = oData.style; 77882 } 77883 } 77884 77885 this.label = oData.label; 77886 77887 this.labelElId = "ygtvlabelel" + this.index; 77888 77889 }, 77890 77891 /** 77892 * Returns the label element 77893 * @for YAHOO.widget.TextNode 77894 * @method getLabelEl 77895 * @return {object} the element 77896 */ 77897 getLabelEl: function() { 77898 return Dom.get(this.labelElId); 77899 }, 77900 77901 // overrides YAHOO.widget.Node 77902 getContentHtml: function() { 77903 var sb = []; 77904 sb[sb.length] = this.href ? '<a' : '<span'; 77905 sb[sb.length] = ' id="' + Lang.escapeHTML(this.labelElId) + '"'; 77906 sb[sb.length] = ' class="' + Lang.escapeHTML(this.labelStyle) + '"'; 77907 if (this.href) { 77908 sb[sb.length] = ' href="' + Lang.escapeHTML(this.href) + '"'; 77909 sb[sb.length] = ' target="' + Lang.escapeHTML(this.target) + '"'; 77910 } 77911 if (this.title) { 77912 sb[sb.length] = ' title="' + Lang.escapeHTML(this.title) + '"'; 77913 } 77914 sb[sb.length] = ' >'; 77915 sb[sb.length] = Lang.escapeHTML(this.label); 77916 sb[sb.length] = this.href?'</a>':'</span>'; 77917 return sb.join(""); 77918 }, 77919 77920 77921 77922 /** 77923 * Returns an object which could be used to build a tree out of this node and its children. 77924 * It can be passed to the tree constructor to reproduce this node as a tree. 77925 * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not. 77926 * @method getNodeDefinition 77927 * @return {Object | false} definition of the tree or false if this node or any descendant is defined as dynamic 77928 */ 77929 getNodeDefinition: function() { 77930 var def = YAHOO.widget.TextNode.superclass.getNodeDefinition.call(this); 77931 if (def === false) { return false; } 77932 77933 // Node specific properties 77934 def.label = this.label; 77935 if (this.labelStyle != 'ygtvlabel') { def.style = this.labelStyle; } 77936 if (this.title) { def.title = this.title; } 77937 if (this.href) { def.href = this.href; } 77938 if (this.target != '_self') { def.target = this.target; } 77939 77940 return def; 77941 77942 }, 77943 77944 toString: function() { 77945 return YAHOO.widget.TextNode.superclass.toString.call(this) + ": " + this.label; 77946 }, 77947 77948 // deprecated 77949 onLabelClick: function() { 77950 return false; 77951 }, 77952 refresh: function() { 77953 YAHOO.widget.TextNode.superclass.refresh.call(this); 77954 var label = this.getLabelEl(); 77955 label.innerHTML = this.label; 77956 if (label.tagName.toUpperCase() == 'A') { 77957 label.href = this.href; 77958 label.target = this.target; 77959 } 77960 } 77961 77962 77963 77964 77965}); 77966})(); 77967/** 77968 * A menu-specific implementation that differs from TextNode in that only 77969 * one sibling can be expanded at a time. 77970 * @namespace YAHOO.widget 77971 * @class MenuNode 77972 * @extends YAHOO.widget.TextNode 77973 * @param oData {object} a string or object containing the data that will 77974 * be used to render this node. 77975 * Providing a string is the same as providing an object with a single property named label. 77976 * All values in the oData will be used to set equally named properties in the node 77977 * as long as the node does have such properties, they are not undefined, private or functions. 77978 * All attributes are made available in noderef.data, which 77979 * can be used to store custom attributes. TreeView.getNode(s)ByProperty 77980 * can be used to retrieve a node by one of the attributes. 77981 * @param oParent {YAHOO.widget.Node} this node's parent node 77982 * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded) 77983 * @constructor 77984 */
77985YAHOO.widget.MenuNode = function(oData, oParent, expanded) { 77986 YAHOO.widget.MenuNode.superclass.constructor.call(this,oData,oParent,expanded); 77987 77988 /* 77989 * Menus usually allow only one branch to be open at a time. 77990 */ 77991 this.multiExpand = false; 77992 77993}; 77994 77995YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, { 77996 77997 /** 77998 * The node type 77999 * @property _type 78000 * @private 78001 * @default "MenuNode" 78002 */ 78003 _type: "MenuNode" 78004 78005}); 78006(function () { 78007 var Dom = YAHOO.util.Dom, 78008 Lang = YAHOO.lang, 78009 Event = YAHOO.util.Event; 78010 78011/** 78012 * This implementation takes either a string or object for the 78013 * oData argument. If is it a string, it will use it for the display 78014 * of this node (and it can contain any html code). If the parameter 78015 * is an object,it looks for a parameter called "html" that will be 78016 * used for this node's display. 78017 * @namespace YAHOO.widget 78018 * @class HTMLNode 78019 * @extends YAHOO.widget.Node 78020 * @constructor 78021 * @param oData {object} a string or object containing the data that will 78022 * be used to render this node. 78023 * Providing a string is the same as providing an object with a single property named html. 78024 * All values in the oData will be used to set equally named properties in the node 78025 * as long as the node does have such properties, they are not undefined, private or functions. 78026 * All other attributes are made available in noderef.data, which 78027 * can be used to store custom attributes. TreeView.getNode(s)ByProperty 78028 * can be used to retrieve a node by one of the attributes. 78029 * @param oParent {YAHOO.widget.Node} this node's parent node 78030 * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded) 78031 * @param hasIcon {boolean} specifies whether or not leaf nodes should 78032 * be rendered with or without a horizontal line and/or toggle icon. If the icon 78033 * is not displayed, the content fills the space it would have occupied. 78034 * This option operates independently of the leaf node presentation logic 78035 * for dynamic nodes. 78036 * (deprecated; use oData.hasIcon) 78037 */ 78038var HN = function(oData, oParent, expanded, hasIcon) { 78039 if (oData) { 78040 this.init(oData, oParent, expanded); 78041 this.initContent(oData, hasIcon); 78042 } 78043}; 78044 78045 78046YAHOO.widget.HTMLNode = HN; 78047YAHOO.extend(HN, YAHOO.widget.Node, { 78048 78049 /** 78050 * The CSS class for the html content container. Defaults to ygtvhtml, but 78051 * can be overridden to provide a custom presentation for a specific node. 78052 * @property contentStyle 78053 * @type string 78054 */ 78055 contentStyle: "ygtvhtml", 78056 78057 78058 /** 78059 * The HTML content to use for this node's display 78060 * @property html 78061 * @type string 78062 */ 78063 html: null, 78064 78065/** 78066 * The node type 78067 * @property _type 78068 * @private 78069 * @type string 78070 * @default "HTMLNode" 78071 */ 78072 _type: "HTMLNode", 78073 78074 /** 78075 * Sets up the node label 78076 * @method initContent 78077 * @param oData {object} An html string or object containing an html property 78078 * @param hasIcon {boolean} determines if the node will be rendered with an 78079 * icon or not 78080 */
78081 initContent: function(oData, hasIcon) { 78082 this.setHtml(oData); 78083 this.contentElId = "ygtvcontentel" + this.index; 78084 if (!Lang.isUndefined(hasIcon)) { this.hasIcon = hasIcon; } 78085 78086 this.logger = new YAHOO.widget.LogWriter(this.toString()); 78087 }, 78088 78089 /** 78090 * Synchronizes the node.html, and the node's content 78091 * @method setHtml 78092 * @param o {object |string | HTMLElement } An html string, an object containing an html property or an HTML element 78093 */ 78094 setHtml: function(o) { 78095 this.html = (Lang.isObject(o) && 'html' in o) ? o.html : o; 78096 78097 var el = this.getContentEl(); 78098 if (el) { 78099 if (o.nodeType && o.nodeType == 1 && o.tagName) { 78100 el.innerHTML = ""; 78101 } else { 78102 el.innerHTML = this.html; 78103 } 78104 } 78105 78106 }, 78107 78108 // overrides YAHOO.widget.Node 78109 // If property html is a string, it sets the innerHTML for the node 78110 // If it is an HTMLElement, it defers appending it to the tree until the HTML basic structure is built 78111 getContentHtml: function() { 78112 if (typeof this.html === "string") { 78113 return this.html; 78114 } else { 78115 78116 HN._deferredNodes.push(this); 78117 if (!HN._timer) { 78118 HN._timer = window.setTimeout(function () { 78119 var n; 78120 while((n = HN._deferredNodes.pop())) { 78121 n.getContentEl().appendChild(n.html); 78122 } 78123 HN._timer = null; 78124 },0); 78125 } 78126 return ""; 78127 } 78128 }, 78129 78130 /** 78131 * Returns an object which could be used to build a tree out of this node and its children. 78132 * It can be passed to the tree constructor to reproduce this node as a tree. 78133 * It will return false if any node loads dynamically, regardless of whether it is loaded or not. 78134 * @method getNodeDefinition 78135 * @return {Object | false} definition of the tree or false if any node is defined as dynamic 78136 */ 78137 getNodeDefinition: function() { 78138 var def = HN.superclass.getNodeDefinition.call(this); 78139 if (def === false) { return false; } 78140 def.html = this.html; 78141 return def; 78142 78143 } 78144}); 78145 78146 /** 78147 * An array of HTMLNodes created with HTML Elements that had their rendering 78148 * deferred until the basic tree structure is rendered. 78149 * @property _deferredNodes 78150 * @type YAHOO.widget.HTMLNode[] 78151 * @default [] 78152 * @private 78153 * @static 78154 */ 78155HN._deferredNodes = []; 78156 /** 78157 * A system timer value used to mark whether a deferred operation is pending. 78158 * @property _timer 78159 * @type System Timer 78160 * @default null 78161 * @private 78162 * @static 78163 */ 78164HN._timer = null; 78165})(); 78166(function () { 78167 var Dom = YAHOO.util.Dom, 78168 Lang = YAHOO.lang, 78169 Event = YAHOO.util.Event, 78170 Calendar = YAHOO.widget.Calendar; 78171 78172/** 78173 * A Date-specific implementation that differs from TextNode in that it uses 78174 * YAHOO.widget.Calendar as an in-line editor, if available 78175 * If Calendar is not available, it behaves as a plain TextNode. 78176 * @namespace YAHOO.widget 78177 * @class DateNode 78178 * @extends YAHOO.widget.TextNode 78179 * @param oData {object} a string or object containing the data that will 78180 * be used to render this node. 78181 * Providing a string is the same as providing an object with a single property named label. 78182 * All values in the oData will be used to set equally named properties in the node 78183 * as long as the node does have such properties, they are not undefined, private nor functions. 78184 * All attributes are made available in noderef.data, which 78185 * can be used to store custom attributes. TreeView.getNode(s)ByProperty 78186 * can be used to retrieve a node by one of the attributes. 78187 * @param oParent {YAHOO.widget.Node} this node's parent node 78188 * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded) 78189 * @constructor 78190 */
78191YAHOO.widget.DateNode = function(oData, oParent, expanded) { 78192 YAHOO.widget.DateNode.superclass.constructor.call(this,oData, oParent, expanded); 78193}; 78194 78195YAHOO.extend(YAHOO.widget.DateNode, YAHOO.widget.TextNode, { 78196 78197 /** 78198 * The node type 78199 * @property _type 78200 * @type string 78201 * @private 78202 * @default "DateNode" 78203 */ 78204 _type: "DateNode", 78205 78206 /** 78207 * Configuration object for the Calendar editor, if used. 78208 * See <a href="http://developer.yahoo.com/yui/calendar/#internationalization">http://developer.yahoo.com/yui/calendar/#internationalization</a> 78209 * @property calendarConfig 78210 */ 78211 calendarConfig: null, 78212 78213 78214 78215 /** 78216 * If YAHOO.widget.Calendar is available, it will pop up a Calendar to enter a new date. Otherwise, it falls back to a plain <input> textbox 78217 * @method fillEditorContainer 78218 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78219 * @return void 78220 */ 78221 fillEditorContainer: function (editorData) { 78222 78223 var cal, container = editorData.inputContainer; 78224 78225 if (Lang.isUndefined(Calendar)) { 78226 Dom.replaceClass(editorData.editorPanel,'ygtv-edit-DateNode','ygtv-edit-TextNode'); 78227 YAHOO.widget.DateNode.superclass.fillEditorContainer.call(this, editorData); 78228 return; 78229 } 78230 78231 if (editorData.nodeType != this._type) { 78232 editorData.nodeType = this._type; 78233 editorData.saveOnEnter = false; 78234 78235 editorData.node.destroyEditorContents(editorData); 78236 78237 editorData.inputObject = cal = new Calendar(container.appendChild(document.createElement('div'))); 78238 if (this.calendarConfig) { 78239 cal.cfg.applyConfig(this.calendarConfig,true); 78240 cal.cfg.fireQueue(); 78241 } 78242 cal.selectEvent.subscribe(function () { 78243 this.tree._closeEditor(true); 78244 },this,true); 78245 } else { 78246 cal = editorData.inputObject; 78247 } 78248 78249 editorData.oldValue = this.label; 78250 cal.cfg.setProperty("selected",this.label, false); 78251 78252 var delim = cal.cfg.getProperty('DATE_FIELD_DELIMITER'); 78253 var pageDate = this.label.split(delim); 78254 cal.cfg.setProperty('pagedate',pageDate[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] + delim + pageDate[cal.cfg.getProperty('MDY_YEAR_POSITION') -1]); 78255 cal.cfg.fireQueue(); 78256 78257 cal.render(); 78258 cal.oDomContainer.focus(); 78259 }, 78260 /** 78261 * Returns the value from the input element. 78262 * Overrides Node.getEditorValue. 78263 * @method getEditorValue 78264 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78265 * @return {string} date entered 78266 */ 78267 78268 getEditorValue: function (editorData) { 78269 if (Lang.isUndefined(Calendar)) { 78270 return editorData.inputElement.value; 78271 } else { 78272 var cal = editorData.inputObject, 78273 date = cal.getSelectedDates()[0], 78274 dd = []; 78275 78276 dd[cal.cfg.getProperty('MDY_DAY_POSITION') -1] = date.getDate(); 78277 dd[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] = date.getMonth() + 1; 78278 dd[cal.cfg.getProperty('MDY_YEAR_POSITION') -1] = date.getFullYear(); 78279 return dd.join(cal.cfg.getProperty('DATE_FIELD_DELIMITER')); 78280 } 78281 }, 78282 78283 /** 78284 * Finally displays the newly entered date in the tree. 78285 * Overrides Node.displayEditedValue. 78286 * @method displayEditedValue 78287 * @param value {HTML} date to be displayed and stored in the node. 78288 * This data is added to the node unescaped via the innerHTML property. 78289 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78290 */ 78291 displayEditedValue: function (value,editorData) { 78292 var node = editorData.node; 78293 node.label = value; 78294 node.getLabelEl().innerHTML = value; 78295 }, 78296 78297 /** 78298 * Returns an object which could be used to build a tree out of this node and its children. 78299 * It can be passed to the tree constructor to reproduce this node as a tree. 78300 * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not. 78301 * @method getNodeDefinition 78302 * @return {Object | false} definition of the node or false if this node or any descendant is defined as dynamic 78303 */ 78304 getNodeDefinition: function() { 78305 var def = YAHOO.widget.DateNode.superclass.getNodeDefinition.call(this); 78306 if (def === false) { return false; } 78307 if (this.calendarConfig) { def.calendarConfig = this.calendarConfig; } 78308 return def; 78309 } 78310 78311 78312}); 78313})(); 78314(function () { 78315 var Dom = YAHOO.util.Dom, 78316 Lang = YAHOO.lang, 78317 Event = YAHOO.util.Event, 78318 TV = YAHOO.widget.TreeView, 78319 TVproto = TV.prototype; 78320 78321 /** 78322 * An object to store information used for in-line editing 78323 * for all Nodes of all TreeViews. It contains: 78324 * <ul> 78325 * <li>active {boolean}, whether there is an active cell editor </li> 78326 * <li>
78326whoHasIt {YAHOO.widget.TreeView} TreeView instance that is currently using the editor</li> 78327 * <li>nodeType {string} value of static Node._type property, allows reuse of input element if node is of the same type.</li> 78328 * <li>editorPanel {HTMLelement (<div>)} element holding the in-line editor</li> 78329 * <li>inputContainer {HTMLelement (<div>)} element which will hold the type-specific input element(s) to be filled by the fillEditorContainer method</li> 78330 * <li>buttonsContainer {HTMLelement (<div>)} element which holds the <button> elements for Ok/Cancel. If you don't want any of the buttons, hide it via CSS styles, don't destroy it</li> 78331 * <li>node {YAHOO.widget.Node} reference to the Node being edited</li> 78332 * <li>saveOnEnter {boolean}, whether the Enter key should be accepted as a Save command (Esc. is always taken as Cancel), disable for multi-line input elements </li> 78333 * <li>oldValue {any} value before editing</li> 78334 * </ul> 78335 * Editors are free to use this object to store additional data. 78336 * @property editorData 78337 * @static 78338 * @for YAHOO.widget.TreeView 78339 */ 78340 TV.editorData = { 78341 active:false, 78342 whoHasIt:null, // which TreeView has it 78343 nodeType:null, 78344 editorPanel:null, 78345 inputContainer:null, 78346 buttonsContainer:null, 78347 node:null, // which Node is being edited 78348 saveOnEnter:true, 78349 oldValue:undefined 78350 // Each node type is free to add its own properties to this as it sees fit. 78351 }; 78352 78353 /** 78354 * Validator function for edited data, called from the TreeView instance scope, 78355 * receives the arguments (newValue, oldValue, nodeInstance) 78356 * and returns either the validated (or type-converted) value or undefined. 78357 * An undefined return will prevent the editor from closing 78358 * @property validator 78359 * @type function 78360 * @default null 78361 * @for YAHOO.widget.TreeView 78362 */ 78363 TVproto.validator = null; 78364 78365 /** 78366 * Entry point for initializing the editing plug-in. 78367 * TreeView will call this method on initializing if it exists 78368 * @method _initEditor 78369 * @for YAHOO.widget.TreeView 78370 * @private 78371 */ 78372 78373 TVproto._initEditor = function () { 78374 /** 78375 * Fires when the user clicks on the ok button of a node editor
78376 * @event editorSaveEvent 78377 * @type CustomEvent 78378 * @param oArgs.newValue {mixed} the new value just entered 78379 * @param oArgs.oldValue {mixed} the value originally in the tree 78380 * @param oArgs.node {YAHOO.widget.Node} the node that has the focus 78381 * @for YAHOO.widget.TreeView 78382 */ 78383 this.createEvent("editorSaveEvent", this); 78384 78385 /** 78386 * Fires when the user clicks on the cancel button of a node editor 78387 * @event editorCancelEvent 78388 * @type CustomEvent 78389 * @param {YAHOO.widget.Node} node the node that has the focus 78390 * @for YAHOO.widget.TreeView 78391 */ 78392 this.createEvent("editorCancelEvent", this); 78393 78394 }; 78395 78396 /** 78397 * Entry point of the editing plug-in. 78398 * TreeView will call this method if it exists when a node label is clicked 78399 * @method _nodeEditing 78400 * @param node {YAHOO.widget.Node} the node to be edited 78401 * @return {Boolean} true to indicate that the node is editable and prevent any further bubbling of the click. 78402 * @for YAHOO.widget.TreeView 78403 * @private 78404 */ 78405 78406 78407 78408 TVproto._nodeEditing = function (node) { 78409 if (node.fillEditorContainer && node.editable) { 78410 var ed, topLeft, buttons, button, editorData = TV.editorData; 78411 editorData.active = true; 78412 editorData.whoHasIt = this; 78413 if (!editorData.nodeType) { 78414 // Fixes: http://yuilibrary.com/projects/yui2/ticket/2528945 78415 editorData.editorPanel = ed = this.getEl().appendChild(document.createElement('div')); 78416 Dom.addClass(ed,'ygtv-label-editor'); 78417 ed.tabIndex = 0; 78418 78419 buttons = editorData.buttonsContainer = ed.appendChild(document.createElement('div')); 78420 Dom.addClass(buttons,'ygtv-button-container'); 78421 button = buttons.appendChild(document.createElement('button')); 78422 Dom.addClass(button,'ygtvok'); 78423 button.innerHTML = ' '; 78424 button = buttons.appendChild(document.createElement('button')); 78425 Dom.addClass(button,'ygtvcancel'); 78426 button.innerHTML = ' '; 78427 Event.on(buttons, 'click', function (ev) { 78428 var target = Event.getTarget(ev), 78429 editorData = TV.editorData, 78430 node = editorData.node, 78431 self = editorData.whoHasIt; 78432 self.logger.log('click on editor'); 78433 if (Dom.hasClass(target,'ygtvok')) { 78434 node.logger.log('ygtvok'); 78435 Event.stopEvent(ev); 78436 self._closeEditor(true); 78437 } 78438 if (Dom.hasClass(target,'ygtvcancel')) { 78439 node.logger.log('ygtvcancel'); 78440 Event.stopEvent(ev); 78441 self._closeEditor(false); 78442 } 78443 }); 78444 78445 editorData.inputContainer = ed.appendChild(document.createElement('div')); 78446 Dom.addClass(editorData.inputContainer,'ygtv-input'); 78447 78448 Event.on(ed,'keydown',function (ev) { 78449 var editorData = TV.editorData, 78450 KEY = YAHOO.util.KeyListener.KEY, 78451 self = editorData.whoHasIt; 78452 switch (ev.keyCode) { 78453 case KEY.ENTER: 78454 self.logger.log('ENTER'); 78455 Event.stopEvent(ev); 78456 if (editorData.saveOnEnter) { 78457 self._closeEditor(true); 78458 } 78459 break; 78460 case KEY.ESCAPE: 78461 self.logger.log('ESC'); 78462 Event.stopEvent(ev); 78463 self._closeEditor(false); 78464 break; 78465 } 78466 }); 78467 78468 78469 78470 } else { 78471 ed = editorData.editorPanel; 78472 } 78473 editorData.node = node; 78474 if (editorData.nodeType) { 78475 Dom.removeClass(ed,'ygtv-edit-' + editorData.nodeType); 78476 } 78477 Dom.addClass(ed,' ygtv-edit-' + node._type); 78478 // Fixes: http://yuilibrary.com/projects/yui2/ticket/2528945 78479 Dom.setStyle(ed,'display','block'); 78480 Dom.setXY(ed,Dom.getXY(node.getContentEl())); 78481 // up to here 78482 ed.focus(); 78483 node.fillEditorContainer(editorData); 78484 78485 return true; // If inline editor available, don't do anything else. 78486 } 78487 }; 78488 78489 /** 78490 * Method to be associated with an event (clickEvent, dblClickEvent or enterKeyPressed) to pop up the contents editor 78491 * It calls the corresponding node editNode method. 78492 * @method onEventEditNode 78493 * @param oArgs {object} Object passed as arguments to TreeView event listeners 78494 * @for YAHOO.widget.TreeView 78495 */ 78496 78497 TVproto.onEventEditNode = function (oArgs) { 78498 if (oArgs instanceof YAHOO.widget.Node) { 78499 oArgs.editNode(); 78500 } else if (oArgs.node instanceof YAHOO.widget.Node) { 78501 oArgs.node.editNode(); 78502 } 78503 return false;
78504 }; 78505 78506 /** 78507 * Method to be called when the inline editing is finished and the editor is to be closed 78508 * @method _closeEditor 78509 * @param save {Boolean} true if the edited value is to be saved, false if discarded 78510 * @private 78511 * @for YAHOO.widget.TreeView 78512 */ 78513 78514 TVproto._closeEditor = function (save) { 78515 var ed = TV.editorData, 78516 node = ed.node, 78517 close = true; 78518 // http://yuilibrary.com/projects/yui2/ticket/2528946 78519 // _closeEditor might now be called at any time, even when there is no label editor open 78520 // so we need to ensure there is one. 78521 if (!node || !ed.active) { return; } 78522 if (save) { 78523 close = ed.node.saveEditorValue(ed) !== false; 78524 } else { 78525 this.fireEvent( 'editorCancelEvent', node); 78526 } 78527 78528 if (close) { 78529 Dom.setStyle(ed.editorPanel,'display','none'); 78530 ed.active = false; 78531 node.focus(); 78532 } 78533 }; 78534 78535 /** 78536 * Entry point for TreeView's destroy method to destroy whatever the editing plug-in has created 78537 * @method _destroyEditor 78538 * @private 78539 * @for YAHOO.widget.TreeView 78540 */ 78541 TVproto._destroyEditor = function() { 78542 var ed = TV.editorData; 78543 if (ed && ed.nodeType && (!ed.active || ed.whoHasIt === this)) { 78544 Event.removeListener(ed.editorPanel,'keydown'); 78545 Event.removeListener(ed.buttonContainer,'click'); 78546 ed.node.destroyEditorContents(ed); 78547 document.body.removeChild(ed.editorPanel); 78548 ed.nodeType = ed.editorPanel = ed.inputContainer = ed.buttonsContainer = ed.whoHasIt = ed.node = null; 78549 ed.active = false; 78550 } 78551 }; 78552 78553 var Nproto = YAHOO.widget.Node.prototype; 78554 78555 /** 78556 * Signals if the label is editable. (Ignored on TextNodes with href set.) 78557 * @property editable 78558 * @type boolean 78559 * @for YAHOO.widget.Node 78560 */ 78561 Nproto.editable = false; 78562 78563 /** 78564 * pops up the contents editor, if there is one and the node is declared editable 78565 * @method editNode 78566 * @for YAHOO.widget.Node 78567 */ 78568 78569 Nproto.editNode = function () { 78570 this.tree._nodeEditing(this); 78571 }; 78572 78573 78574 /** Placeholder for a function that should provide the inline node label editor. 78575 * Leaving it set to null will indicate that this node type is not editable. 78576 * It should be overridden by nodes that provide inline editing. 78577 * The Node-specific editing element (input box, textarea or whatever) should be inserted into editorData.inputContainer. 78578 * @method fillEditorContainer 78579 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78580 * @return void 78581 * @for YAHOO.widget.Node 78582 */ 78583 Nproto.fillEditorContainer = null; 78584 78585 78586 /** 78587 * Node-specific destroy function to empty the contents of the inline editor panel. 78588 * This function is the worst case alternative that will purge all possible events and remove the editor contents. 78589 * Method Event.purgeElement is somewhat costly so if it can be replaced by specifc Event.removeListeners, it is better to do so. 78590 * @method destroyEditorContents 78591 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78592 * @for YAHOO.widget.Node 78593 */ 78594 Nproto.destroyEditorContents = function (editorData) { 78595 // In the worst case, if the input editor (such as the Calendar) has no destroy method 78596 // we can only try to remove all possible events on it. 78597 Event.purgeElement(editorData.inputContainer,true); 78598 editorData.inputContainer.innerHTML = ''; 78599 }; 78600 78601 /** 78602 * Saves the value entered into the editor. 78603 * @method saveEditorValue 78604 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78605 * @return {false or none} a return of exactly false will prevent the editor from closing 78606 * @for YAHOO.widget.Node 78607 */ 78608 Nproto.saveEditorValue = function (editorData) { 78609 var node = editorData.node, 78610 value, 78611 validator = node.tree.validator; 78612 78613 value = this.getEditorValue(editorData); 78614
78615 if (Lang.isFunction(validator)) { 78616 value = validator(value,editorData.oldValue,node); 78617 if (Lang.isUndefined(value)) { 78618 return false; 78619 } 78620 } 78621 78622 if (this.tree.fireEvent( 'editorSaveEvent', { 78623 newValue:value, 78624 oldValue:editorData.oldValue, 78625 node:node 78626 }) !== false) { 78627 this.displayEditedValue(value,editorData); 78628 } 78629 }; 78630 78631 78632 /** 78633 * Returns the value(s) from the input element(s) . 78634 * Should be overridden by each node type. 78635 * @method getEditorValue 78636 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78637 * @return {any} value entered 78638 * @for YAHOO.widget.Node 78639 */ 78640 78641 Nproto.getEditorValue = function (editorData) { 78642 }; 78643 78644 /** 78645 * Finally displays the newly edited value(s) in the tree. 78646 * Should be overridden by each node type. 78647 * @method displayEditedValue 78648 * @param value {HTML} value to be displayed and stored in the node 78649 * This data is added to the node unescaped via the innerHTML property. 78650 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78651 * @for YAHOO.widget.Node 78652 */ 78653 Nproto.displayEditedValue = function (value,editorData) { 78654 }; 78655 78656 var TNproto = YAHOO.widget.TextNode.prototype; 78657 78658 78659 78660 /** 78661 * Places an <input> textbox in the input container and loads the label text into it. 78662 * @method fillEditorContainer 78663 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78664 * @return void 78665 * @for YAHOO.widget.TextNode 78666 */ 78667 TNproto.fillEditorContainer = function (editorData) { 78668 78669 var input; 78670 // If last node edited is not of the same type as this one, delete it and fill it with our editor
78671 if (editorData.nodeType != this._type) { 78672 editorData.nodeType = this._type; 78673 editorData.saveOnEnter = true; 78674 editorData.node.destroyEditorContents(editorData); 78675 78676 editorData.inputElement = input = editorData.inputContainer.appendChild(document.createElement('input')); 78677 78678 } else { 78679 // if the last node edited was of the same time, reuse the input element. 78680 input = editorData.inputElement; 78681 } 78682 editorData.oldValue = this.label; 78683 input.value = this.label; 78684 input.focus(); 78685 input.select(); 78686 }; 78687 78688 /** 78689 * Returns the value from the input element. 78690 * Overrides Node.getEditorValue. 78691 * @method getEditorValue 78692 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78693 * @return {string} value entered 78694 * @for YAHOO.widget.TextNode 78695 */ 78696 78697 TNproto.getEditorValue = function (editorData) { 78698 return editorData.inputElement.value; 78699 }; 78700 78701 /** 78702 * Finally displays the newly edited value in the tree. 78703 * Overrides Node.displayEditedValue. 78704 * @method displayEditedValue 78705 * @param value {string} value to be displayed and stored in the node 78706 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78707 * @for YAHOO.widget.TextNode 78708 */ 78709 TNproto.displayEditedValue = function (value,editorData) { 78710 var node = editorData.node; 78711 node.label = value; 78712 node.getLabelEl().innerHTML = value; 78713 }; 78714 78715 /** 78716 * Destroys the contents of the inline editor panel. 78717 * Overrides Node.destroyEditorContent. 78718 * Since we didn't set any event listeners on this inline editor, it is more efficient to avoid the generic method in Node. 78719 * @method destroyEditorContents 78720 * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information 78721 * @for YAHOO.widget.TextNode 78722 */ 78723 TNproto.destroyEditorContents = function (editorData) { 78724 editorData.inputContainer.innerHTML = ''; 78725 }; 78726})(); 78727/** 78728 * A static factory class for tree view expand/collapse animations 78729 * @class TVAnim 78730 * @static 78731 */ 78732YAHOO.widget.TVAnim = function() { 78733 return { 78734 /** 78735 * Constant for the fade in animation 78736 * @property FADE_IN 78737 * @type string 78738 * @static 78739 */ 78740 FADE_IN: "TVFadeIn", 78741 78742 /** 78743 * Constant for the fade out animation 78744 * @property FADE_OUT 78745 * @type string 78746 * @static 78747 */ 78748 FADE_OUT: "TVFadeOut", 78749 78750 /** 78751 * Returns a ygAnim instance of the given type 78752 * @method getAnim 78753 * @param type {string} the type of animation 78754 * @param el {HTMLElement} the element to element (probably the children div) 78755 * @param callback {function} function to invoke when the animation is done. 78756 * @return {YAHOO.util.Animation} the animation instance 78757 * @static 78758 */ 78759 getAnim: function(type, el, callback) { 78760 if (YAHOO.widget[type]) { 78761 return new YAHOO.widget[type](el, callback); 78762 } else { 78763 return null; 78764 } 78765 }, 78766 78767 /** 78768 * Returns true if the specified animation class is available 78769 * @method isValid 78770 * @param type {string} the type of animation 78771 * @return {boolean} true if valid, false if not 78772 * @static 78773 */ 78774 isValid: function(type) { 78775 return (YAHOO.widget[type]); 78776 } 78777 }; 78778} (); 78779/** 78780 * A 1/2 second fade-in animation. 78781 * @class TVFadeIn 78782 * @constructor 78783 * @param el {HTMLElement} the element to animate 78784 * @param callback {function} function to invoke when the animation is finished 78785 */ 78786YAHOO.widget.TVFadeIn = function(el, callback) { 78787 /** 78788 * The element to animate 78789 * @property el 78790 * @type HTMLElement 78791 */ 78792 this.el = el; 78793 78794 /** 78795 * the callback to invoke when the animation is complete 78796 * @property callback 78797 * @type function 78798 */ 78799 this.callback = callback; 78800 78801 this.logger = new YAHOO.widget.LogWriter(this.toString()); 78802}; 78803 78804YAHOO.widget.TVFadeIn.prototype = { 78805 /** 78806 * Performs the animation 78807 * @method animate 78808 */ 78809 animate: function() { 78810 var tvanim = this; 78811 78812 var s = this.el.style; 78813 s.opacity = 0.1; 78814 s.filter = "alpha(opacity=10)"; 78815 s.display = ""; 78816 78817 var dur = 0.4; 78818 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}
78818, dur); 78819 a.onComplete.subscribe( function() { tvanim.onComplete(); } ); 78820 a.animate(); 78821 }, 78822 78823 /** 78824 * Clean up and invoke callback 78825 * @method onComplete 78826 */ 78827 onComplete: function() { 78828 this.callback(); 78829 }, 78830 78831 /** 78832 * toString 78833 * @method toString 78834 * @return {string} the string representation of the instance 78835 */ 78836 toString: function() { 78837 return "TVFadeIn"; 78838 } 78839}; 78840/** 78841 * A 1/2 second fade out animation. 78842 * @class TVFadeOut 78843 * @constructor 78844 * @param el {HTMLElement} the element to animate 78845 * @param callback {Function} function to invoke when the animation is finished 78846 */ 78847YAHOO.widget.TVFadeOut = function(el, callback) { 78848 /** 78849 * The element to animate 78850 * @property el 78851 * @type HTMLElement 78852 */ 78853 this.el = el; 78854 78855 /** 78856 * the callback to invoke when the animation is complete 78857 * @property callback 78858 * @type function 78859 */ 78860 this.callback = callback; 78861 78862 this.logger = new YAHOO.widget.LogWriter(this.toString()); 78863}; 78864 78865YAHOO.widget.TVFadeOut.prototype = { 78866 /** 78867 * Performs the animation 78868 * @method animate 78869 */ 78870 animate: function() { 78871 var tvanim = this; 78872 var dur = 0.4; 78873 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur); 78874 a.onComplete.subscribe( function() { tvanim.onComplete(); } ); 78875 a.animate(); 78876 }, 78877 78878 /** 78879 * Clean up and invoke callback 78880 * @method onComplete 78881 */ 78882 onComplete: function() { 78883 var s = this.el.style; 78884 s.display = "none"; 78885 s.opacity = 1; 78886 s.filter = "alpha(opacity=100)"; 78887 this.callback(); 78888 }, 78889 78890 /** 78891 * toString 78892 * @method toString 78893 * @return {string} the string representation of the instance 78894 */ 78895 toString: function() { 78896 return "TVFadeOut"; 78897 } 78898}; 78899YAHOO.register("treeview", YAHOO.widget.TreeView, {version: "2.9.0", build: "2800"}); 78900/* 78901Copyright (c) 2011, Yahoo! Inc. All rights reserved. 78902Code licensed under the BSD License: 78903http://developer.yahoo.com/yui/license.html 78904version: 2.9.0 78905*/ 78906/** 78907 * Utilities for cookie management 78908 * @namespace YAHOO.util 78909 * @module cookie 78910 */ 78911YAHOO.namespace("util"); 78912 78913/** 78914 * Cookie utility. 78915 * @class Cookie 78916 * @static 78917 */ 78918YAHOO.util.Cookie = { 78919 78920 //------------------------------------------------------------------------- 78921 // Private Methods 78922 //------------------------------------------------------------------------- 78923 78924 /** 78925 * Creates a cookie string that can be assigned into document.cookie. 78926 * @param {String} name The name of the cookie. 78927 * @param {String} value The value of the cookie. 78928 * @param {Boolean} encodeValue True to encode the value, false to leave as-is. 78929 * @param {Object} options (Optional) Options for the cookie. 78930 * @return {String} The formatted cookie string. 78931 * @method _createCookieString 78932 * @private 78933 * @static 78934 */ 78935 _createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ { 78936 78937 //shortcut 78938 var lang = YAHOO.lang, 78939 text = encodeURIComponent(name) + "=" + (encodeValue ? encodeURIComponent(value) : value); 78940 78941 78942 if (lang.isObject(options)){ 78943 //expiration date 78944 if (options.expires instanceof Date){ 78945 text += "; expires=" + options.expires.toUTCString(); 78946 } 78947 78948 //path 78949 if (lang.isString(options.path) && options.path !== ""){ 78950 text += "; path=" + options.path; 78951 } 78952 78953 //domain 78954 if (lang.isString(options.domain) && options.domain !== ""){ 78955 text += "; domain=" + options.domain; 78956 } 78957 78958 //secure 78959 if (options.secure === true){ 78960 text += "; secure"; 78961 } 78962 } 78963 78964 return text; 78965 }, 78966 78967 /** 78968 * Formats a cookie value for an object containing multiple values. 78969 * @param {Object} hash An object of key-value pairs to create a string for. 78970 * @return {String} A string suitable for use as a cookie value.
78971 * @method _createCookieHashString 78972 * @private 78973 * @static 78974 */ 78975 _createCookieHashString : function (hash /*:Object*/) /*:String*/ { 78976 78977 //shortcuts 78978 var lang = YAHOO.lang; 78979 78980 if (!lang.isObject(hash)){ 78981 throw new TypeError("Cookie._createCookieHashString(): Argument must be an object."); 78982 } 78983 78984 var text /*:Array*/ = []; 78985 78986 for (var key in hash){ 78987 if (lang.hasOwnProperty(hash, key) && !lang.isFunction(hash[key]) && !lang.isUndefined(hash[key])){ 78988 text.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(hash[key]))); 78989 } 78990 } 78991 78992 return text.join("&"); 78993 }, 78994 78995 /** 78996 * Parses a cookie hash string into an object. 78997 * @param {String} text The cookie hash string to parse. The string should already be URL-decoded. 78998 * @return {Object} An object containing entries for each cookie value. 78999 * @method _parseCookieHash 79000 * @private 79001 * @static 79002 */ 79003 _parseCookieHash : function (text /*:String*/) /*:Object*/ { 79004 79005 var hashParts /*:Array*/ = text.split("&"), 79006 hashPart /*:Array*/ = null, 79007 hash /*:Object*/ = {}; 79008 79009 if (text.length > 0){ 79010 for (var i=0, len=hashParts.length; i < len; i++){ 79011 hashPart = hashParts[i].split("="); 79012 hash[decodeURIComponent(hashPart[0])] = decodeURIComponent(hashPart[1]); 79013 } 79014 } 79015 79016 return hash; 79017 }, 79018 79019 /** 79020 * Parses a cookie string into an object representing all accessible cookies. 79021 * @param {String} text The cookie string to parse. 79022 * @param {Boolean} decode (Optional) Indicates if the cookie values should be decoded or not. Default is true. 79023 * @return {Object} An object containing entries for each accessible cookie. 79024 * @method _parseCookieString 79025 * @private 79026 * @static 79027 */ 79028 _parseCookieString : function (text /*:String*/, decode /*:Boolean*/) /*:Object*/ { 79029 79030 var cookies /*:Object*/ = {}; 79031 79032 if (YAHOO.lang.isString(text) && text.length > 0) { 79033 79034 var decodeValue = (decode === false ? function(s){return s;} : decodeURIComponent); 79035 79036 //if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)){ 79037 var cookieParts /*:Array*/ = text.split(/;\s/g), 79038 cookieName /*:String*/ = null, 79039 cookieValue /*:String*/ = null, 79040 cookieNameValue /*:Array*/ = null; 79041 79042 for (var i=0, len=cookieParts.length; i < len; i++){ 79043 79044 //check for normally-formatted cookie (name-value) 79045 cookieNameValue = cookieParts[i].match(/([^=]+)=/i); 79046 if (cookieNameValue instanceof Array){ 79047 try { 79048 cookieName = decodeURIComponent(cookieNameValue[1]); 79049 cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length+1)); 79050 } catch (ex){ 79051 //ignore the entire cookie - encoding is likely invalid 79052 } 79053 } else { 79054 //means the cookie does not have an "=", so treat it as a boolean flag 79055 cookieName = decodeURIComponent(cookieParts[i]); 79056 cookieValue = ""; 79057 } 79058 cookies[cookieName] = cookieValue; 79059 } 79060 //} 79061 } 79062 79063 return cookies; 79064 }, 79065 79066 //------------------------------------------------------------------------- 79067 // Public Methods 79068 //------------------------------------------------------------------------- 79069 79070 /** 79071 * Determines if the cookie with the given name exists. This is useful for 79072 * Boolean cookies (those that do not follow the name=value convention). 79073 * @param {String} name The name of the cookie to check. 79074 * @return {Boolean} True if the cookie exists, false if not. 79075 * @method exists 79076 * @static 79077 */ 79078 exists: function(name) { 79079 79080 if (!YAHOO.lang.isString(name) || name === ""){ 79081 throw new TypeError("Cookie.exists(): Cookie name must be a non-empty string."); 79082 } 79083 79084 var cookies /*:Object*/ = this._parseCookieString(document.cookie, true); 79085 79086 return cookies.hasOwnProperty(name); 79087 }, 79088 79089 /** 79090 * Returns the cookie value for the given name. 79091 * @param {String} name The name of the cookie to retrieve. 79092 * @param {Object|Function}
79092 options (Optional) An object containing one or more 79093 * cookie options: raw (true/false) and converter (a function). 79094 * The converter function is run on the value before returning it. The 79095 * function is not used if the cookie doesn't exist. The function can be 79096 * passed instead of the options object for backwards compatibility. 79097 * @return {Variant} If no converter is specified, returns a string or null if 79098 * the cookie doesn't exist. If the converter is specified, returns the value 79099 * returned from the converter or null if the cookie doesn't exist. 79100 * @method get 79101 * @static 79102 */ 79103 get : function (name /*:String*/, options /*:Variant*/) /*:Variant*/{ 79104 79105 var lang = YAHOO.lang, 79106 converter; 79107 79108 if (lang.isFunction(options)) { 79109 converter = options; 79110 options = {}; 79111 } else if (lang.isObject(options)) { 79112 converter = options.converter; 79113 } else { 79114 options = {}; 79115 } 79116 79117 var cookies /*:Object*/ = this._parseCookieString(document.cookie, !options.raw); 79118 79119 if (!lang.isString(name) || name === ""){ 79120 throw new TypeError("Cookie.get(): Cookie name must be a non-empty string."); 79121 } 79122 79123 if (lang.isUndefined(cookies[name])) { 79124 return null; 79125 } 79126 79127 if (!lang.isFunction(converter)){ 79128 return cookies[name]; 79129 } else { 79130 return converter(cookies[name]); 79131 } 79132 }, 79133 79134 /** 79135 * Returns the value of a subcookie. 79136 * @param {String} name The name of the cookie to retrieve. 79137 * @param {String} subName The name of the subcookie to retrieve. 79138 * @param {Function} converter (Optional) A function to run on the value before returning 79139 * it. The function is not used if the cookie doesn't exist. 79140 * @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie 79141 * doesn't exist, null if also returned. If no converter is specified and the 79142 * subcookie exists, a string is returned. If a converter is specified and the 79143 * subcookie exists, the value returned from the converter is returned. 79144 * @method getSub 79145 * @static 79146 */ 79147 getSub : function (name, subName, converter) { 79148 79149 var lang = YAHOO.lang, 79150 hash = this.getSubs(name); 79151 79152 if (hash !== null) { 79153 79154 if (!lang.isString(subName) || subName === ""){ 79155 throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string."); 79156 } 79157 79158 if (lang.isUndefined(hash[subName])){ 79159 return null; 79160 } 79161 79162 if (!lang.isFunction(converter)){ 79163 return hash[subName]; 79164 } else { 79165 return converter(hash[subName]); 79166 } 79167 } else { 79168 return null; 79169 } 79170 79171 }, 79172 79173 /**
79174 * Returns an object containing name-value pairs stored in the cookie with the given name. 79175 * @param {String} name The name of the cookie to retrieve. 79176 * @return {Object} An object of name-value pairs if the cookie with the given name 79177 * exists, null if it does not. 79178 * @method getSubs 79179 * @static 79180 */ 79181 getSubs : function (name /*:String*/) /*:Object*/ { 79182 79183 var isString = YAHOO.lang.isString; 79184 79185 //check cookie name 79186 if (!isString(name) || name === ""){ 79187 throw new TypeError("Cookie.getSubs(): Cookie name must be a non-empty string."); 79188 } 79189 79190 var cookies = this._parseCookieString(document.cookie, false); 79191 if (isString(cookies[name])){ 79192 return this._parseCookieHash(cookies[name]); 79193 } 79194 return null; 79195 }, 79196 79197 /** 79198 * Removes a cookie from the machine by setting its expiration date to 79199 * sometime in the past. 79200 * @param {String} name The name of the cookie to remove. 79201 * @param {Object} options (Optional) An object containing one or more 79202 * cookie options: path (a string), domain (a string), 79203 * and secure (true/false). The expires option will be overwritten 79204 * by the method. 79205 * @return {String} The created cookie string. 79206 * @method remove 79207 * @static 79208 */ 79209 remove : function (name /*:String*/, options /*:Object*/) /*:String*/ { 79210 79211 //check cookie name 79212 if (!YAHOO.lang.isString(name) || name === ""){ 79213 throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string."); 79214 } 79215 79216 //set options - clone options so the original isn't affected 79217 options = YAHOO.lang.merge(options || {}, { 79218 expires: new Date(0) 79219 }); 79220 79221 //set cookie 79222 return this.set(name, "", options); 79223 }, 79224 79225 /** 79226 * Removes a subcookie with a given name. Removing the last subcookie 79227 * won't remove the entire cookie unless options.removeIfEmpty is true. 79228 * @param {String} name The name of the cookie in which the subcookie exists. 79229 * @param {String} subName The name of the subcookie to remove. 79230 * @param {Object} options (Optional) An object containing one or more 79231 * cookie options: path (a string), domain (a string), expires (a Date object), 79232 * removeIfEmpty (true/false), and secure (true/false). This must be the same 79233 * settings as the original subcookie. 79234 * @return {String} The created cookie string. 79235 * @method removeSub 79236 * @static 79237 */ 79238 removeSub : function(name /*:String*/, subName /*:String*/, options /*:Object*/) /*:String*/ { 79239 79240 var lang = YAHOO.lang; 79241 79242 options = options || {}; 79243 79244 //check cookie name 79245 if (!lang.isString(name) || name === ""){ 79246 throw new TypeError("Cookie.removeSub(): Cookie name must be a non-empty string."); 79247 } 79248 79249 //check subcookie name 79250 if (!lang.isString(subName) || subName === ""){ 79251 throw new TypeError("Cookie.removeSub(): Subcookie name must be a non-empty string."); 79252 } 79253 79254 //get all subcookies for this cookie 79255 var subs = this.getSubs(name); 79256 79257 //delete the indicated subcookie 79258 if (lang.isObject(subs) && lang.hasOwnProperty(subs, subName)){ 79259 delete subs[subName]; 79260 79261 if (!options.removeIfEmpty) { 79262 //reset the cookie 79263 79264 return this.setSubs(name, subs, options); 79265 } else { 79266 //reset the cookie if there are subcookies left, else remove 79267 for (var key in subs){ 79268 if (lang.hasOwnProperty(subs, key) && !lang.isFunction(subs[key]) && !lang.isUndefined(subs[key])){ 79269 return this.setSubs(name, subs, options); 79270 } 79271 } 79272 79273 return this.remove(name, options); 79274 } 79275 } else { 79276 return ""; 79277 } 79278 79279 }, 79280 79281 /** 79282 * Sets a cookie with a given name and value. 79283 * @param {String} name The name of the cookie to set. 79284 * @param {Variant} value The value to set for the cookie. 79285 * @param {Object}
79285 options (Optional) An object containing one or more 79286 * cookie options: path (a string), domain (a string), expires (a Date object), 79287 * raw (true/false), and secure (true/false). 79288 * @return {String} The created cookie string. 79289 * @method set 79290 * @static 79291 */ 79292 set : function (name /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ { 79293 79294 var lang = YAHOO.lang; 79295 79296 options = options || {}; 79297 79298 if (!lang.isString(name)){ 79299 throw new TypeError("Cookie.set(): Cookie name must be a string."); 79300 } 79301 79302 if (lang.isUndefined(value)){ 79303 throw new TypeError("Cookie.set(): Value cannot be undefined."); 79304 } 79305 79306 var text /*:String*/ = this._createCookieString(name, value, !options.raw, options); 79307 document.cookie = text; 79308 return text; 79309 }, 79310 79311 /** 79312 * Sets a sub cookie with a given name to a particular value. 79313 * @param {String} name The name of the cookie to set. 79314 * @param {String} subName The name of the subcookie to set. 79315 * @param {Variant} value The value to set. 79316 * @param {Object} options (Optional) An object containing one or more 79317 * cookie options: path (a string), domain (a string), expires (a Date object), 79318 * and secure (true/false). 79319 * @return {String} The created cookie string. 79320 * @method setSub 79321 * @static 79322 */ 79323 setSub : function (name /*:String*/, subName /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ { 79324 79325 var lang = YAHOO.lang; 79326 79327 if (!lang.isString(name) || name === ""){ 79328 throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string."); 79329 } 79330 79331 if (!lang.isString(subName) || subName === ""){ 79332 throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string."); 79333 } 79334 79335 if (lang.isUndefined(value)){ 79336 throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined."); 79337 } 79338 79339 var hash /*:Object*/ = this.getSubs(name); 79340 79341 if (!lang.isObject(hash)){ 79342 hash = {}; 79343 } 79344 79345 hash[subName] = value; 79346 79347 return this.setSubs(name, hash, options); 79348 79349 }, 79350 79351 /** 79352 * Sets a cookie with a given name to contain a hash of name-value pairs. 79353 * @param {String} name The name of the cookie to set. 79354 * @param {Object} value An object containing name-value pairs. 79355 * @param {Object} options (Optional) An object containing one or more 79356 * cookie options: path (a string), domain (a string), expires (a Date object), 79357 * and secure (true/false). 79358 * @return {String} The created cookie string. 79359 * @method setSubs 79360 * @static 79361 */ 79362 setSubs : function (name /*:String*/, value /*:Object*/, options /*:Object*/) /*:String*/ { 79363 79364 var lang = YAHOO.lang; 79365 79366 if (!lang.isString(name)){ 79367 throw new TypeError("Cookie.setSubs(): Cookie name must be a string."); 79368 } 79369 79370 if (!lang.isObject(value)){ 79371 throw new TypeError("Cookie.setSubs(): Cookie value must be an object."); 79372 } 79373 79374 var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options); 79375 document.cookie = text; 79376 return text; 79377 } 79378 79379}; 79380 79381YAHOO.register("cookie", YAHOO.util.Cookie, {version: "2.9.0", build: "2800"}); 79382/* 79383Copyright (c) 2011, Yahoo! Inc. All rights reserved. 79384Code licensed under the BSD License: 79385http://developer.yahoo.com/yui/license.html 79386version: 2.9.0 79387*/ 79388/*extern ActiveXObject, __flash_unloadHandler, __flash_savedUnloadHandler */ 79389/*! 79390 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/ 79391 * 79392 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License: 79393 * http://www.opensource.org/licenses/mit-license.php
79394 * @namespace YAHOO 79395 */ 79396 79397YAHOO.namespace("deconcept"); 79398 79399YAHOO.deconcept = YAHOO.deconcept || {}; 79400 79401if(typeof YAHOO.deconcept.util == "undefined" || !YAHOO.deconcept.util) 79402{ 79403 YAHOO.deconcept.util = {}; 79404} 79405 79406if(typeof YAHOO.deconcept.SWFObjectUtil == "undefined" || !YAHOO.deconcept.SWFObjectUtil) 79407{ 79408 YAHOO.deconcept.SWFObjectUtil = {}; 79409} 79410 79411YAHOO.deconcept.SWFObject = function(swf, id, w, h, ver, c, quality, xiRedirectUrl, redirectUrl, detectKey) 79412{ 79413 if(!document.getElementById) { return; } 79414 this.DETECT_KEY = detectKey ? detectKey : 'detectflash'; 79415 this.skipDetect = YAHOO.deconcept.util.getRequestParameter(this.DETECT_KEY); 79416 this.params = {}; 79417 this.variables = {}; 79418 this.attributes = []; 79419 if(swf) { this.setAttribute('swf', swf); } 79420 if(id) { this.setAttribute('id', id); } 79421 if(w) { this.setAttribute('width', w); } 79422 if(h) { this.setAttribute('height', h); } 79423 if(ver) { this.setAttribute('version', new YAHOO.deconcept.PlayerVersion(ver.toString().split("."))); } 79424 this.installedVer = YAHOO.deconcept.SWFObjectUtil.getPlayerVersion(); 79425 if (!window.opera && document.all && this.installedVer.major > 7) 79426 { 79427 // only add the onunload cleanup if the Flash Player version supports External Interface and we are in IE 79428 YAHOO.deconcept.SWFObject.doPrepUnload = true; 79429 } 79430 if(c) 79431 { 79432 this.addParam('bgcolor', c); 79433 } 79434 var q = quality ? quality : 'high'; 79435 this.addParam('quality', q); 79436 this.setAttribute('useExpressInstall', false); 79437 this.setAttribute('doExpressInstall', false); 79438 var xir = (xiRedirectUrl) ? xiRedirectUrl : window.location; 79439 this.setAttribute('xiRedirectUrl', xir); 79440 this.setAttribute('redirectUrl', ''); 79441 if(redirectUrl) 79442 { 79443 this.setAttribute('redirectUrl', redirectUrl); 79444 } 79445}; 79446 79447YAHOO.deconcept.SWFObject.prototype = 79448{ 79449 useExpressInstall: function(path) 79450 { 79451 this.xiSWFPath = !path ? "expressinstall.swf" : path; 79452 this.setAttribute('useExpressInstall', true); 79453 }, 79454 setAttribute: function(name, value){ 79455 this.attributes[name] = value; 79456 }, 79457 getAttribute: function(name){ 79458 return this.attributes[name]; 79459 }, 79460 addParam: function(name, value){ 79461 this.params[name] = value; 79462 }, 79463 getParams: function(){ 79464 return this.params; 79465 }, 79466 addVariable: function(name, value){ 79467 this.variables[name] = value; 79468 }, 79469 getVariable: function(name){ 79470 return this.variables[name]; 79471 }, 79472 getVariables: function(){ 79473 return this.variables; 79474 }, 79475 getVariablePairs: function(){ 79476 var variablePairs = []; 79477 var key; 79478 var variables = this.getVariables(); 79479 for(key in variables) 79480 { 79481 if(variables.hasOwnProperty(key)) 79482 { 79483 variablePairs[variablePairs.length] = YAHOO.lang.escapeHTML(key || '') +"="+ YAHOO.lang.escapeHTML(encodeURIComponent(variables[key] || '')); 79484 } 79485 } 79486 return variablePairs; 79487 }, 79488 getSWFHTML: function() { 79489 var swfNode = ""; 79490 var params = {}; 79491 var key = ""; 79492 var pairs = ""; 79493 if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture 79494 if (this.getAttribute("doExpressInstall")) { 79495 this.addVariable("MMplayerType", "PlugIn"); 79496 this.setAttribute('swf', this.xiSWFPath); 79497 } 79498 swfNode = '<embed type="application/x-shockwave-flash" src="'+ YAHOO.lang.escapeHTML(this.getAttribute('swf') || '') +'" width="'+ YAHOO.lang.escapeHTML(this.getAttribute('width') || '') +'" height="'+ YAHOO.lang.escapeHTML(this.getAttribute('height') || '') +'" style="'+ YAHOO.lang.escapeHTML(this.getAttribute('style') || '') +'"'; 79499 swfNode += ' id="'+ YAHOO.lang.escapeHTML(this.getAttribute('id') || '') +'" name="'+ YAHOO.lang.escapeHTML(this.getAttribute('id') || '') +'" '; 79500 params = this.getParams(); 79501 for(key in params) 79502 { 79503 if(params.hasOwnProperty(key)) 79504 { 79505 swfNode += YAHOO.lang.escapeHTML(key || '') +'="'+ YAHOO.lang.escapeHTML(params[key] || '') +'" '; 79506 } 79507 } 79508 pairs = this.getVariablePairs().join("&"); 79509 if (pairs.length > 0){ swfNode += 'flashvars="'+ pairs +'"'; } 79510 swfNode += '/>'; 79511 } else { // PC IE 79512 if (this.getAttribute("doExpressInstall")) { 79513 this.addVariable("MMplayerType", "ActiveX"); 79514 this.setAttribute('swf', this.xiSWFPath); 79515 } 79516 swfNode = '<object id="'+ YAHOO.lang.escapeHTML(this.getAttribute('id') || '') +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+ YAHOO.lang.escapeHTML(this.getAttribute('width') || '') +'" height="'+ YAHOO.lang.escapeHTML(this.getAttribute('height') || '') +'" style="'+ YAHOO.lang.escapeHTML(this.getAttribute('style') || '') +'">'; 79517 swfNode += '<param name="movie" value="'+ YAHOO.lang.escapeHTML(this.getAttribute('swf') || '') +'" />'; 79518 params = this.getParams(); 79519 for(key in params) 79520 { 79521 if(params.hasOwnProperty(key)) 79522 { 79523 swfNode += '<param name="'+ YAHOO.lang.escapeHTML(key || '') +'" value="'+ YAHOO.lang.escapeHTML(params[key] || '') +'" />'; 79524 } 79525 } 79526 pairs = this.getVariablePairs().join("&"); 79527 if(pairs.length > 0) {swfNode += '<param name="flashvars" value="'+ pairs +'" />';} 79528 swfNode += "</object>"; 79529 } 79530 return swfNode; 79531 }, 79532 write: function(elementId) 79533 { 79534 if(this.getAttribute('useExpressInstall')) { 79535 // check to see if we need to do an express install 79536 var expressInstallReqVer = new YAHOO.deconcept.PlayerVersion([6,0,65]); 79537 if (this.installedVer.versionIsValid(expressInstallReqVer) && !this.installedVer.versionIsValid(this.getAttribute('version'))) { 79538 this.setAttribute('doExpressInstall', true); 79539 this.addVariable("MMredirectURL", escape(this.getAttribute('xiRedirectUrl'))); 79540 document.title = document.title.slice(0, 47) + " - Flash Player Installation"; 79541 this.addVariable("MMdoctitle", document.title); 79542 } 79543 } 79544 if(this.skipDetect || this.getAttribute('doExpressInstall') || this.installedVer.versionIsValid(this.getAttribute('version'))) 79545 { 79546 var n = (typeof elementId == 'string') ? document.getElementById(elementId) : elementId;
79547 n.innerHTML = this.getSWFHTML(); 79548 return true; 79549 } 79550 else 79551 { 79552 if(this.getAttribute('redirectUrl') !== "") 79553 { 79554 document.location.replace(this.getAttribute('redirectUrl')); 79555 } 79556 } 79557 return false; 79558 } 79559}; 79560 79561/* ---- detection functions ---- */ 79562YAHOO.deconcept.SWFObjectUtil.getPlayerVersion = function() 79563{ 79564 var axo = null; 79565 var PlayerVersion = new YAHOO.deconcept.PlayerVersion([0,0,0]); 79566 if(navigator.plugins && navigator.mimeTypes.length) 79567 { 79568 var x = navigator.plugins["Shockwave Flash"]; 79569 if(x && x.description) 79570 { 79571 PlayerVersion = new YAHOO.deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split(".")); 79572 } 79573 } 79574 else if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0) 79575 { // if Windows CE 79576 var counter = 3; 79577 while(axo) 79578 { 79579 try 79580 { 79581 counter++; 79582 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+ counter); 79583// document.write("player v: "+ counter); 79584 PlayerVersion = new YAHOO.deconcept.PlayerVersion([counter,0,0]); 79585 } 79586 catch(e) 79587 { 79588 axo = null; 79589 } 79590 } 79591 } 79592 else 79593 { // Win IE (non mobile) 79594 // do minor version lookup in IE, but avoid fp6 crashing issues 79595 // see http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/ 79596 try 79597 { 79598 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); 79599 } 79600 catch(e) 79601 { 79602 try 79603 { 79604 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); 79605 PlayerVersion = new YAHOO.deconcept.PlayerVersion([6,0,21]); 79606 axo.AllowScriptAccess = "always"; // error if player version < 6.0.47 (thanks to Michael Williams @ Adobe for this code) 79607 } 79608 catch(e) 79609 { 79610 if(PlayerVersion.major == 6) 79611 { 79612 return PlayerVersion; 79613 } 79614 } 79615 try 79616 { 79617 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); 79618 } 79619 catch(e) {} 79620 } 79621 79622 if(axo !== null) 79623 { 79624 PlayerVersion = new YAHOO.deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(",")); 79625 } 79626 } 79627 return PlayerVersion; 79628}; 79629 79630YAHOO.deconcept.PlayerVersion = function(arrVersion) 79631{ 79632 this.major = arrVersion[0] !== null ? parseInt(arrVersion[0], 0) : 0; 79633 this.minor = arrVersion[1] !== null ? parseInt(arrVersion[1], 0) : 0; 79634 this.rev = arrVersion[2] !== null ? parseInt(arrVersion[2], 0) : 0; 79635}; 79636 79637YAHOO.deconcept.PlayerVersion.prototype.versionIsValid = function(fv) 79638{ 79639 if(this.major < fv.major) 79640 { 79641 return false; 79642 } 79643 if(this.major > fv.major) 79644 { 79645 return true; 79646 } 79647 if(this.minor < fv.minor) 79648 { 79649 return false; 79650 } 79651 if(this.minor > fv.minor) 79652 { 79653 return true; 79654 } 79655 if(this.rev < fv.rev) 79656 { 79657 return false; 79658 } 79659 return true; 79660}; 79661 79662/* ---- get value of query string param ---- */ 79663YAHOO.deconcept.util = 79664{ 79665 getRequestParameter: function(param) 79666 { 79667 var q = document.location.search || document.location.hash; 79668 if(param === null) { return q; } 79669 if(q) 79670 { 79671 var pairs = q.substring(1).split("&"); 79672 for(var i=0; i < pairs.length; i++) 79673 { 79674 if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) 79675 { 79676 return pairs[i].substring((pairs[i].indexOf("=") + 1)); 79677 } 79678 } 79679 } 79680 return ""; 79681 } 79682}; 79683 79684/* fix for video streaming bug */ 79685YAHOO.deconcept.SWFObjectUtil.cleanupSWFs = function() 79686{ 79687 var objects = document.getElementsByTagName("OBJECT"); 79688 for(var i = objects.length - 1; i >= 0; i--) 79689 { 79690 objects[i].style.display = 'none'; 79691 for(var x in objects[i]) 79692 { 79693 if(typeof objects[i][x] == 'function') 79694 { 79695 objects[i][x] = function(){}; 79696 } 79697 } 79698 } 79699}; 79700 79701// fixes bug in some fp9 versions see http://blog.deconcept.com/2006/07/28/swfobject-143-released/ 79702if(YAHOO.deconcept.SWFObject.doPrepUnload) 79703{ 79704 if(!YAHOO.deconcept.unloadSet) 79705 { 79706 YAHOO.deconcept.SWFObjectUtil.prepUnload = function() 79707 { 79708 __flash_unloadHandler = function(){}; 79709 __flash_savedUnloadHandler = function(){}; 79710 window.attachEvent("onunload", YAHOO.deconcept.SWFObjectUtil.cleanupSWFs); 79711 }; 79712 window.attachEvent("onbeforeunload", YAHOO.deconcept.SWFObjectUtil.prepUnload); 79713 YAHOO.deconcept.unloadSet = true; 79714 } 79715} 79716 79717/* add document.getElementById if needed (mobile IE < 5) */ 79718if(!document.getElementById && document.all) 79719{ 79720 document.getElementById = function(id) { return document.all[id]; }; 79721} 79722/** 79723 * Wraps Flash embedding functionality and allows communication with SWF through 79724 * attributes. 79725 * 79726 * @namespace YAHOO.widget 79727 * @class FlashAdapter 79728 * @uses YAHOO.util.AttributeProvider 79729 */ 79730YAHOO.widget.FlashAdapter = function(swfURL, containerID, attributes, buttonSkin) 79731{ 79732 79733 this._queue = this._queue || []; 79734 this._events = this._events || {}; 79735 this._configs = this._configs || {}; 79736 attributes = attributes || {}; 79737
79738 //the Flash Player external interface code from Adobe doesn't play nicely 79739 //with the default value, yui-gen, in IE 79740 this._id = attributes.id = attributes.id || YAHOO.util.Dom.generateId(null, "yuigen"); 79741 attributes.version = attributes.version || "9.0.45"; 79742 attributes.backgroundColor = attributes.backgroundColor || "#ffffff"; 79743 79744 //we can't use the initial attributes right away 79745 //so save them for once the SWF finishes loading 79746 this._attributes = attributes; 79747 79748 this._swfURL = swfURL; 79749 this._containerID = containerID; 79750 79751 //embed the SWF file in the page 79752 this._embedSWF(this._swfURL, this._containerID, attributes.id, attributes.version, 79753 attributes.backgroundColor, attributes.expressInstall, attributes.wmode, buttonSkin); 79754 79755 /** 79756 * Fires when the SWF is initialized and communication is possible. 79757 * @event contentReady 79758 */ 79759 //Fix for iframe cross-domain issue with FF2x 79760 try 79761 { 79762 this.createEvent("contentReady"); 79763 } 79764 catch(e){} 79765}; 79766 79767YAHOO.widget.FlashAdapter.owners = YAHOO.widget.FlashAdapter.owners || {}; 79768 79769YAHOO.extend(YAHOO.widget.FlashAdapter, YAHOO.util.AttributeProvider, 79770{ 79771 /** 79772 * The URL of the SWF file. 79773 * @property _swfURL
79774 * @type String 79775 * @private 79776 */ 79777 _swfURL: null, 79778 79779 /** 79780 * The ID of the containing DIV. 79781 * @property _containerID 79782 * @type String 79783 * @private 79784 */ 79785 _containerID: null, 79786 79787 /** 79788 * A reference to the embedded SWF file. 79789 * @property _swf 79790 * @private 79791 */ 79792 _swf: null, 79793 79794 /** 79795 * The id of this instance. 79796 * @property _id 79797 * @type String 79798 * @private 79799 */ 79800 _id: null, 79801 79802 /** 79803 * Indicates whether the SWF has been initialized and is ready 79804 * to communicate with JavaScript 79805 * @property _initialized 79806 * @type Boolean 79807 * @private 79808 */ 79809 _initialized: false, 79810 79811 /** 79812 * The initializing attributes are stored here until the SWF is ready. 79813 * @property _attributes 79814 * @type Object 79815 * @private 79816 */ 79817 _attributes: null, //the intializing attributes 79818 79819 /** 79820 * Public accessor to the unique name of the FlashAdapter instance. 79821 * 79822 * @method toString 79823 * @return {String} Unique name of the FlashAdapter instance. 79824 */ 79825 toString: function() 79826 { 79827 return "FlashAdapter " + this._id; 79828 }, 79829 79830 /** 79831 * Nulls out the entire FlashAdapter instance and related objects and removes attached 79832 * event listeners and clears out DOM elements inside the container. After calling 79833 * this method, the instance reference should be expliclitly nulled by implementer, 79834 * as in myChart = null. Use with caution! 79835 * 79836 * @method destroy 79837 */ 79838 destroy: function() 79839 { 79840 //kill the Flash Player instance 79841 if(this._swf) 79842 { 79843 var container = YAHOO.util.Dom.get(this._containerID); 79844 container.removeChild(this._swf); 79845 } 79846 79847 var instanceName = this._id; 79848 79849 //null out properties 79850 for(var prop in this) 79851 { 79852 if(YAHOO.lang.hasOwnProperty(this, prop)) 79853 { 79854 this[prop] = null; 79855 } 79856 } 79857 79858 YAHOO.log("FlashAdapter instance destroyed: " + instanceName); 79859 }, 79860 79861 /** 79862 * Embeds the SWF in the page and associates it with this instance. 79863 * 79864 * @method _embedSWF 79865 * @private 79866 */ 79867 _embedSWF: function(swfURL, containerID, swfID, version, backgroundColor, expressInstall, wmode, buttonSkin) 79868 { 79869 //standard SWFObject embed 79870 var swfObj = new YAHOO.deconcept.SWFObject(swfURL, swfID, "100%", "100%", version, backgroundColor); 79871 79872 if(expressInstall) 79873 { 79874 swfObj.useExpressInstall(expressInstall); 79875 } 79876 79877 //make sure we can communicate with ExternalInterface 79878 swfObj.addParam("allowScriptAccess", "always"); 79879 79880 if(wmode) 79881 { 79882 swfObj.addParam("wmode", wmode); 79883 } 79884 79885 swfObj.addParam("menu", "false"); 79886 79887 //again, a useful ExternalInterface trick 79888 swfObj.addVariable("allowedDomain", document.location.hostname); 79889 79890 //tell the SWF which HTML element it is in 79891 swfObj.addVariable("YUISwfId", swfID); 79892 79893 // set the name of the function to call when the swf has an event 79894 swfObj.addVariable("YUIBridgeCallback", "YAHOO.widget.FlashAdapter.eventHandler"); 79895 if (buttonSkin) { 79896 swfObj.addVariable("buttonSkin", buttonSkin); 79897 } 79898 var container = YAHOO.util.Dom.get(containerID); 79899 var result = swfObj.write(container); 79900 if(result) 79901 { 79902 this._swf = YAHOO.util.Dom.get(swfID); 79903 YAHOO.widget.FlashAdapter.owners[swfID] = this; 79904 } 79905 else 79906 { 79907 YAHOO.log("Unable to load SWF " + swfURL); 79908 } 79909 }, 79910 79911 /** 79912 * Handles or re-dispatches events received from the SWF. 79913 * 79914 * @method _eventHandler 79915 * @private 79916 */ 79917 _eventHandler: function(event) 79918 { 79919 var type = event.type; 79920 switch(type) 79921 { 79922 case "swfReady": 79923 this._loadHandler(); 79924 return; 79925 case "log": 79926 YAHOO.log(event.message, event.category, this.toString()); 79927 return; 79928 } 79929 79930 79931 //be sure to return after your case or the event will automatically fire! 79932 this.fireEvent(type, event); 79933 }, 79934 79935 /** 79936 * Called when the SWF has been initialized. 79937 * 79938 * @method _loadHandler 79939 * @private 79940 */ 79941 _loadHandler: function() 79942 { 79943 this._initialized = false; 79944 this._initAttributes(this._attributes); 79945 this.setAttributes(this._attributes, true); 79946 79947 this._initialized = true; 79948 this.fireEvent("contentReady"); 79949 }, 79950 79951 set: function(name, value) 79952 { 79953 //save all the attributes in case the swf reloads 79954 //so that we can pass them in again 79955 this._attributes[name] = value; 79956 YAHOO.widget.FlashAdapter.superclass.set.call(this, name, value); 79957 }, 79958 79959 /** 79960 * Initializes the attributes. 79961 * 79962 * @method _initAttributes 79963 * @private 79964 */ 79965 _initAttributes: function(attributes) 79966 { 79967 //should be overridden if other attributes need to be set up 79968 79969 /** 79970 * @attribute wmode 79971 * @description Sets the window mode of the Flash Player control. May be 79972 * "window", "opaque", or "transparent". Only available in the constructor 79973 * because it may not be set after Flash Player has been embedded in the page.
79974 * @type String 79975 */ 79976 79977 /** 79978 * @attribute expressInstall 79979 * @description URL pointing to a SWF file that handles Flash Player's express 79980 * install feature. Only available in the constructor because it may not be 79981 * set after Flash Player has been embedded in the page. 79982 * @type String 79983 */ 79984 79985 /** 79986 * @attribute version 79987 * @description Minimum required version for the SWF file. Only available in the constructor because it may not be 79988 * set after Flash Player has been embedded in the page. 79989 * @type String 79990 */ 79991 79992 /** 79993 * @attribute backgroundColor 79994 * @description The background color of the SWF. Only available in the constructor because it may not be 79995 * set after Flash Player has been embedded in the page. 79996 * @type String 79997 */ 79998 79999 /** 80000 * @attribute altText 80001 * @description The alternative text to provide for screen readers and other assistive technology. 80002 * @type String 80003 */ 80004 this.getAttributeConfig("altText", 80005 { 80006 method: this._getAltText 80007 }); 80008 this.setAttributeConfig("altText", 80009 { 80010 method: this._setAltText 80011 }); 80012 80013 /** 80014 * @attribute swfURL 80015 * @description Absolute or relative URL to the SWF displayed by the FlashAdapter. Only available in the constructor because it may not be 80016 * set after Flash Player has been embedded in the page.
80017 * @type String 80018 */ 80019 this.getAttributeConfig("swfURL", 80020 { 80021 method: this._getSWFURL 80022 }); 80023 }, 80024 80025 /** 80026 * Getter for swfURL attribute. 80027 * 80028 * @method _getSWFURL 80029 * @private 80030 */ 80031 _getSWFURL: function() 80032 { 80033 return this._swfURL; 80034 }, 80035 80036 /** 80037 * Getter for altText attribute. 80038 * 80039 * @method _getAltText 80040 * @private 80041 */ 80042 _getAltText: function() 80043 { 80044 return this._swf.getAltText(); 80045 }, 80046 80047 /** 80048 * Setter for altText attribute. 80049 * 80050 * @method _setAltText 80051 * @private 80052 */ 80053 _setAltText: function(value) 80054 { 80055 return this._swf.setAltText(value); 80056 } 80057}); 80058 80059 80060/** 80061 * Receives event messages from SWF and passes them to the correct instance 80062 * of FlashAdapter. 80063 * 80064 * @method YAHOO.widget.FlashAdapter.eventHandler 80065 * @static 80066 * @private 80067 */ 80068YAHOO.widget.FlashAdapter.eventHandler = function(elementID, event) 80069{ 80070 80071 if(!YAHOO.widget.FlashAdapter.owners[elementID]) 80072 { 80073 //fix for ie: if owner doesn't exist yet, try again in a moment 80074 setTimeout(function() { YAHOO.widget.FlashAdapter.eventHandler( elementID, event ); }, 0); 80075 } 80076 else 80077 { 80078 YAHOO.widget.FlashAdapter.owners[elementID]._eventHandler(event); 80079 } 80080}; 80081 80082/** 80083 * The number of proxy functions that have been created. 80084 * @static 80085 * @private 80086 */ 80087YAHOO.widget.FlashAdapter.proxyFunctionCount = 0; 80088 80089/** 80090 * Creates a globally accessible function that wraps a function reference. 80091 * Returns the proxy function's name as a string for use by the SWF through 80092 * ExternalInterface. 80093 * 80094 * @method YAHOO.widget.FlashAdapter.createProxyFunction 80095 * @static 80096 * @private 80097 */ 80098YAHOO.widget.FlashAdapter.createProxyFunction = function(func) 80099{ 80100 var index = YAHOO.widget.FlashAdapter.proxyFunctionCount; 80101 YAHOO.widget.FlashAdapter["proxyFunction" + index] = function() 80102 { 80103 return func.apply(null, arguments); 80104 }; 80105 YAHOO.widget.FlashAdapter.proxyFunctionCount++; 80106 return "YAHOO.widget.FlashAdapter.proxyFunction" + index.toString(); 80107}; 80108 80109/** 80110 * Removes a function created with createProxyFunction() 80111 * 80112 * @method YAHOO.widget.FlashAdapter.removeProxyFunction 80113 * @static 80114 * @private 80115 */ 80116YAHOO.widget.FlashAdapter.removeProxyFunction = function(funcName) 80117{ 80118 //quick error check 80119 if(!funcName || funcName.indexOf("YAHOO.widget.FlashAdapter.proxyFunction") < 0) 80120 { 80121 return; 80122 } 80123 80124 funcName = funcName.substr(26); 80125 YAHOO.widget.FlashAdapter[funcName] = null; 80126}; 80127/** 80128 * The YUI Uploader Control 80129 * @module uploader 80130 * @description <p>YUI Uploader provides file upload functionality that goes beyond the basic browser-based methods. 80131 * Specifically, the YUI Uploader allows for: 80132 * <ol> 80133 * <li> Multiple file selection in a single "Open File" dialog.</li> 80134 * <li> File extension filters to facilitate the user's selection.</li> 80135 * <li> Progress tracking for file uploads.</li> 80136 * <li> A range of file metadata: filename, size, date created, date modified, and author.</li> 80137 * <li> A set of events dispatched on various aspects of the file upload process: file selection, upload progress, upload completion, etc.</li> 80138 * <li> Inclusion of additional data in the file upload POST request.</li> 80139 * <li> Faster file upload on broadband connections due to the modified SEND buffer size.</li> 80140 * <li> Same-page server response upon completion of the file upload.</li> 80141 * </ol> 80142 * </p> 80143 * @title Uploader 80144 * @namespace YAHOO.widget 80145 * @requires yahoo, dom, element, event 80146 */ 80147/** 80148 * Uploader class for the YUI Uploader component. 80149 * 80150 * @namespace YAHOO.widget 80151 * @class Uploader 80152 * @uses YAHOO.widget.FlashAdapter 80153 * @constructor 80154 * @param containerId {HTMLElement} Container element for the Flash Player instance. 80155 * @param buttonSkin {String} [optional]. If defined, the uploader is 80156 * rendered as a button. This parameter must provide the URL of a button 80157 * skin sprite image. Acceptable types are: jpg, gif, png and swf. The 80158 * sprite is divided evenly into four sections along its height (e.g., if 80159 * the sprite is 200 px tall, it's divided into four sections 50px each). 80160 * Each section is used as a skin for a specific state of the button: top 80161 * section is "up", second section is "over", third section is "down", and 80162 * fourth section is "disabled". 80163 * If the parameter is not supplied, the uploader is rendered transparent, 80164 * and it's the developer's responsibility to create a visible UI below it. 80165 * @param forceTransparent {Boolean} This parameter, if true, forces the Flash 80166 * UI to be rendered with wmode set to "transparent". This behavior is useful 80167 * in conjunction with non-rectangular button skins with PNG transparency. 80168 * The parameter is false by default, and ignored if no buttonSkin is defined. 80169 */ 80170YAHOO.widget.Uploader = function(containerId, buttonSkin, forceTransparent) 80171{ 80172 var newWMode = "window"; 80173 80174 if (!(buttonSkin) || (buttonSkin && forceTransparent)) { 80175 newWMode = "transparent";
80176 } 80177 80178 80179 YAHOO.widget.Uploader.superclass.constructor.call(this, YAHOO.widget.Uploader.SWFURL, containerId, {wmode:newWMode}, buttonSkin); 80180 80181 /** 80182 * Fires when the mouse is pressed over the Uploader. 80183 * Only fires when the Uploader UI is enabled and 80184 * the render type is 'transparent'. 80185 * 80186 * @event mouseDown 80187 * @param event.type {String} The event type 80188 */ 80189 this.createEvent("mouseDown"); 80190 80191 /** 80192 * Fires when the mouse is released over the Uploader. 80193 * Only fires when the Uploader UI is enabled and 80194 * the render type is 'transparent'. 80195 * 80196 * @event mouseUp 80197 * @param event.type {String} The event type 80198 */ 80199 this.createEvent("mouseUp"); 80200 80201 /** 80202 * Fires when the mouse rolls over the Uploader. 80203 * 80204 * @event rollOver 80205 * @param event.type {String} The event type 80206 */ 80207 this.createEvent("rollOver"); 80208 80209 /** 80210 * Fires when the mouse rolls out of the Uploader. 80211 * 80212 * @event rollOut 80213 * @param event.type {String} The event type 80214 */ 80215 this.createEvent("rollOut"); 80216 80217 /** 80218 * Fires when the uploader is clicked. 80219 * 80220 * @event click 80221 * @param event.type {String} The event type 80222 */ 80223 this.createEvent("click"); 80224 80225 /** 80226 * Fires when the user has finished selecting files in the "Open File" dialog. 80227 * 80228 * @event fileSelect 80229 * @param event.type {String} The event type 80230 * @param event.fileList {Object} A dictionary of objects with file information 80231 * @param event.fileList[].size {Number} File size in bytes for a specific file in fileList 80232 * @param event.fileList[].cDate {Date} Creation date for a specific file in fileList 80233 * @param event.fileList[].mDate {Date} Modification date for a specific file in fileList 80234 * @param event.fileList[].name {String} File name for a specific file in fileList 80235 * @param event.fileList[].id {String} Unique file id of a specific file in fileList 80236 */ 80237 this.createEvent("fileSelect"); 80238 80239 /** 80240 * Fires when an upload of a specific file has started. 80241 * 80242 * @event uploadStart 80243 * @param event.type {String} The event type 80244 * @param event.id {String} The id of the file that's started to upload 80245 */ 80246 this.createEvent("uploadStart"); 80247 80248 /** 80249 * Fires when new information about the upload progress for a specific file is available. 80250 * 80251 * @event uploadProgress 80252 * @param event.type {String} The event type 80253 * @param event.id {String} The id of the file with which the upload progress data is associated 80254 * @param bytesLoaded {Number} The number of bytes of the file uploaded so far 80255 * @param bytesTotal {Number} The total size of the file 80256 */ 80257 this.createEvent("uploadProgress"); 80258 80259 /** 80260 * Fires when an upload for a specific file is cancelled. 80261 * 80262 * @event uploadCancel 80263 * @param event.type {String} The event type 80264 * @param event.id {String} The id of the file with which the upload has been cancelled. 80265 */ 80266 this.createEvent("uploadCancel"); 80267 80268 /** 80269 * Fires when an upload for a specific file is complete. 80270 * 80271 * @event uploadComplete 80272 * @param event.type {String} The event type 80273 * @param event.id {String} The id of the file for which the upload has been completed. 80274 */ 80275 this.createEvent("uploadComplete"); 80276 80277 /** 80278 * Fires when the server sends data in response to a completed upload. 80279 * 80280 * @event uploadCompleteData 80281 * @param event.type {String} The event type 80282 * @param event.id {String} The id of the file for which the upload has been completed. 80283 * @param event.data {String} The raw data returned by the server in response to the upload. 80284 */ 80285 this.createEvent("uploadCompleteData"); 80286 80287 /** 80288 * Fires when an upload error occurs. 80289 * 80290 * @event uploadError 80291 * @param event.type {String} The event type 80292 * @param event.id {String} The id of the file that was being uploaded when the error has occurred. 80293 * @param event.status {String} The status message associated with the error. 80294 */ 80295 this.createEvent("uploadError"); 80296} 80297 80298/** 80299 * Location of the Uploader SWF 80300 * 80301 * @property Chart.SWFURL 80302 * @private 80303 * @static 80304 * @final 80305 * @default "assets/uploader.swf" 80306 */ 80307YAHOO.widget.Uploader.SWFURL = "assets/uploader.swf"; 80308 80309YAHOO.extend(YAHOO.widget.Uploader, YAHOO.widget.FlashAdapter, 80310{ 80311/** 80312 * Starts the upload of the file specified by fileID to the location specified by uploadScriptPath. 80313 * 80314 * @param fileID {String} The id of the file to start uploading. 80315 * @param uploadScriptPath {String} The URL of the upload location. 80316 * @param method {String} Either "GET" or "POST", specifying how the variables accompanying the file upload POST request should be submitted. "GET" by default. 80317 * @param vars {Object}
80317 The object containing variables to be sent in the same request as the file upload. 80318 * @param fieldName {String} The name of the variable in the POST request containing the file data. "Filedata" by default. 80319 * </code> 80320 */ 80321 upload: function(fileID, uploadScriptPath, method, vars, fieldName) 80322 { 80323 this._swf.upload(fileID, uploadScriptPath, method, vars, fieldName); 80324 }, 80325 80326/** 80327 * Starts the upload of the files specified by fileIDs, or adds them to a currently running queue. The upload queue is automatically managed. 80328 * 80329 * @param fileIDs {Array} The ids of the files to start uploading. 80330 * @param uploadScriptPath {String} The URL of the upload location. 80331 * @param method {String} Either "GET" or "POST", specifying how the variables accompanying the file upload POST request should be submitted. "GET" by default. 80332 * @param vars {Object} The object containing variables to be sent in the same request as the file upload. 80333 * @param fieldName {String} The name of the variable in the POST request containing the file data. "Filedata" by default. 80334 * </code> 80335 */ 80336 uploadThese: function(fileIDs, uploadScriptPath, method, vars, fieldName) 80337 { 80338 this._swf.uploadThese(fileIDs, uploadScriptPath, method, vars, fieldName); 80339 }, 80340 80341/** 80342 * Starts uploading all files in the queue. If this function is called, the upload queue is automatically managed. 80343 * 80344 * @param uploadScriptPath {String} The URL of the upload location. 80345 * @param method {String} Either "GET" or "POST", specifying how the variables accompanying the file upload POST request should be submitted. "GET" by default. 80346 * @param vars {Object} The object containing variables to be sent in the same request as the file upload. 80347 * @param fieldName {String} The name of the variable in the POST request containing the file data. "Filedata" by default. 80348 * </code> 80349 */ 80350 uploadAll: function(uploadScriptPath, method, vars, fieldName) 80351 { 80352 this._swf.uploadAll(uploadScriptPath, method, vars, fieldName); 80353 }, 80354 80355/** 80356 * Cancels the upload of a specified file. If no file id is specified, all ongoing uploads are cancelled. 80357 * 80358 * @param fileID {String} The ID of the file whose upload should be cancelled. 80359 */ 80360 cancel: function(fileID) 80361 { 80362 this._swf.cancel(fileID); 80363 }, 80364 80365/** 80366 * Clears the list of files queued for upload. 80367 * 80368 */ 80369 clearFileList: function() 80370 { 80371 this._swf.clearFileList(); 80372 }, 80373 80374/** 80375 * Removes the specified file from the upload queue. 80376 * 80377 * @param fileID {String} The id of the file to remove from the upload queue. 80378 */ 80379 removeFile: function (fileID) 80380 { 80381 this._swf.removeFile(fileID); 80382 }, 80383 80384/** 80385 * Turns the logging functionality on. 80386 * Uses Flash internal trace logging, as well as YUI Logger, if available. 80387 * 80388 * @param allowLogging {Boolean} If true, logs are output; otherwise, no logs are produced. 80389 */ 80390 setAllowLogging: function (allowLogging) 80391 { 80392 this._swf.setAllowLogging(allowLogging); 80393 }, 80394 80395/** 80396 * Sets the number of simultaneous uploads when using uploadAll() 80397 * The minimum value is 1, and maximum value is 5. The default value is 2. 80398 * 80399 * @param simUploadLimit {int} Number of simultaneous uploads, between 1 and 5. 80400 */ 80401 setSimUploadLimit : function (simUploadLimit) 80402 { 80403 this._swf.setSimUploadLimit(simUploadLimit); 80404 }, 80405 80406/** 80407 * Sets the flag allowing users to select multiple files for the upload. 80408 * 80409 * @param allowMultipleFiles {Boolean} If true, multiple files can be selected. False by default. 80410 */ 80411 setAllowMultipleFiles : function (allowMultipleFiles) 80412 { 80413 this._swf.setAllowMultipleFiles(allowMultipleFiles); 80414 }, 80415 80416/** 80417 * Sets the file filters for the "Browse" dialog. 80418 * 80419 * @param newFilterArray An array of sets of key-value pairs of the form 80420 * {extensions: extensionString, description: descriptionString, [optional]macType: macTypeString} 80421 * The extensions string is a semicolon-delimited list of elements of the form "*.xxx", 80422 * e.g. "*.jpg;*.gif;*.png". 80423 */ 80424 setFileFilters : function (fileFilters) 80425 { 80426 this._swf.setFileFilters(fileFilters); 80427 }, 80428 80429 /** 80430 * Enables the mouse events on the Uploader. 80431 * If the uploader is being rendered as a button, 80432 * then the button's skin is set to "up" 80433 * (first section of the button skin sprite). 80434 * 80435 */ 80436 enable : function () 80437 { 80438 this._swf.enable(); 80439 }, 80440 80441 /** 80442 * Disables the mouse events on the Uploader. 80443 * If the uploader is being rendered as a button, 80444 * then the button's skin is set to "disabled" 80445 * (fourth section of the button skin sprite). 80446 * 80447 */ 80448 disable : function () 80449 { 80450 this._swf.disable(); 80451 } 80452}); 80453YAHOO.register("uploader", YAHOO.widget.Uploader, {version: "2.9.0", build: "2800"}); 80454/* 80455Copyright (c) 2011, Yahoo! Inc. All rights reserved. 80456Code licensed under the BSD License:
80457http://developer.yahoo.com/yui/license.html 80458version: 2.9.0 80459*/ 80460(function () { 80461 80462 /** 80463 * Config is a utility used within an Object to allow the implementer to 80464 * maintain a list of local configuration properties and listen for changes 80465 * to those properties dynamically using CustomEvent. The initial values are 80466 * also maintained so that the configuration can be reset at any given point 80467 * to its initial state. 80468 * @namespace YAHOO.util 80469 * @class Config 80470 * @constructor 80471 * @param {Object} owner The owner Object to which this Config Object belongs 80472 */ 80473 YAHOO.util.Config = function (owner) { 80474 80475 if (owner) { 80476 this.init(owner); 80477 } 80478 80479 if (!owner) { YAHOO.log("No owner specified for Config object", "error", "Config"); } 80480 80481 }; 80482 80483 80484 var Lang = YAHOO.lang, 80485 CustomEvent = YAHOO.util.CustomEvent, 80486 Config = YAHOO.util.Config; 80487 80488 80489 /** 80490 * Constant representing the CustomEvent type for the config changed event. 80491 * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT 80492 * @private 80493 * @static 80494 * @final 80495 */ 80496 Config.CONFIG_CHANGED_EVENT = "configChanged"; 80497 80498 /** 80499 * Constant representing the boolean type string 80500 * @property YAHOO.util.Config.BOOLEAN_TYPE 80501 * @private 80502 * @static 80503 * @final 80504 */ 80505 Config.BOOLEAN_TYPE = "boolean"; 80506 80507 Config.prototype = { 80508 80509 /** 80510 * Object reference to the owner of this Config Object 80511 * @property owner 80512 * @type Object 80513 */ 80514 owner: null, 80515 80516 /** 80517 * Boolean flag that specifies whether a queue is currently 80518 * being executed 80519 * @property queueInProgress 80520 * @type Boolean 80521 */ 80522 queueInProgress: false, 80523 80524 /** 80525 * Maintains the local collection of configuration property objects and 80526 * their specified values 80527 * @property config 80528 * @private 80529 * @type Object 80530 */ 80531 config: null, 80532 80533 /** 80534 * Maintains the local collection of configuration property objects as 80535 * they were initially applied. 80536 * This object is used when resetting a property. 80537 * @property initialConfig 80538 * @private 80539 * @type Object 80540 */ 80541 initialConfig: null, 80542 80543 /** 80544 * Maintains the local, normalized CustomEvent queue 80545 * @property eventQueue 80546 * @private 80547 * @type Object 80548 */ 80549 eventQueue: null, 80550 80551 /** 80552 * Custom Event, notifying subscribers when Config properties are set 80553 * (setProperty is called without the silent flag 80554 * @event configChangedEvent 80555 */ 80556 configChangedEvent: null, 80557 80558 /** 80559 * Initializes the configuration Object and all of its local members. 80560 * @method init 80561 * @param {Object} owner The owner Object to which this Config 80562 * Object belongs 80563 */ 80564 init: function (owner) { 80565 80566 this.owner = owner; 80567 80568 this.configChangedEvent = 80569 this.createEvent(Config.CONFIG_CHANGED_EVENT); 80570 80571 this.configChangedEvent.signature = CustomEvent.LIST; 80572 this.queueInProgress = false; 80573 this.config = {}; 80574 this.initialConfig = {}; 80575 this.eventQueue = []; 80576 80577 }, 80578 80579 /** 80580 * Validates that the value passed in is a Boolean. 80581 * @method checkBoolean 80582 * @param {Object} val The value to validate 80583 * @return {Boolean} true, if the value is valid 80584 */ 80585 checkBoolean: function (val) { 80586 return (typeof val == Config.BOOLEAN_TYPE); 80587 }, 80588 80589 /** 80590 * Validates that the value passed in is a number. 80591 * @method checkNumber 80592 * @param {Object} val The value to validate 80593 * @return {Boolean} true, if the value is valid 80594 */ 80595 checkNumber: function (val) { 80596 return (!isNaN(val)); 80597 }, 80598 80599 /** 80600 * Fires a configuration property event using the specified value. 80601 * @method fireEvent 80602 * @private 80603 * @param {String} key The configuration property's name 80604 * @param {value} Object The value of the correct type for the property 80605 */ 80606 fireEvent: function ( key, value ) { 80607 YAHOO.log("Firing Config event: " + key + "=" + value, "info", "Config"); 80608 var property = this.config[key]; 80609 80610 if (property && property.event) { 80611 property.event.fire(value); 80612 } 80613 }, 80614 80615 /** 80616 * Adds a property to the Config Object's private config hash. 80617 * @method addProperty 80618 * @param {String} key The configuration property's name 80619 * @param {Object} propertyObject The Object containing all of this 80620 * property's arguments 80621 */ 80622 addProperty: function ( key, propertyObject ) { 80623 key = key.toLowerCase(); 80624 YAHOO.log("Added property: " + key, "info", "Config"); 80625 80626 this.config[key] = propertyObject; 80627 80628 propertyObject.event = this.createEvent(key, { scope: this.owner }); 80629 propertyObject.event.signature = CustomEvent.LIST; 80630 80631 80632 propertyObject.key = key; 80633 80634 if (propertyObject.handler) { 80635 propertyObject.event.subscribe(propertyObject.handler,
80636 this.owner); 80637 } 80638 80639 this.setProperty(key, propertyObject.value, true); 80640 80641 if (! propertyObject.suppressEvent) { 80642 this.queueProperty(key, propertyObject.value); 80643 } 80644 80645 }, 80646 80647 /** 80648 * Returns a key-value configuration map of the values currently set in 80649 * the Config Object. 80650 * @method getConfig 80651 * @return {Object} The current config, represented in a key-value map 80652 */ 80653 getConfig: function () { 80654 80655 var cfg = {}, 80656 currCfg = this.config, 80657 prop, 80658 property; 80659 80660 for (prop in currCfg) { 80661 if (Lang.hasOwnProperty(currCfg, prop)) { 80662 property = currCfg[prop]; 80663 if (property && property.event) { 80664 cfg[prop] = property.value; 80665 } 80666 } 80667 } 80668 80669 return cfg; 80670 }, 80671 80672 /** 80673 * Returns the value of specified property. 80674 * @method getProperty 80675 * @param {String} key The name of the property 80676 * @return {Object} The value of the specified property 80677 */ 80678 getProperty: function (key) { 80679 var property = this.config[key.toLowerCase()]; 80680 if (property && property.event) { 80681 return property.value; 80682 } else { 80683 return undefined; 80684 } 80685 }, 80686 80687 /** 80688 * Resets the specified property's value to its initial value. 80689 * @method resetProperty 80690 * @param {String} key The name of the property 80691 * @return {Boolean} True is the property was reset, false if not 80692 */ 80693 resetProperty: function (key) { 80694 key = key.toLowerCase(); 80695 80696 var property = this.config[key]; 80697 80698 if (property && property.event) { 80699 if (key in this.initialConfig) { 80700 this.setProperty(key, this.initialConfig[key]); 80701 return true; 80702 } 80703 } else { 80704 return false; 80705 } 80706 }, 80707 80708 /** 80709 * Sets the value of a property. If the silent property is passed as 80710 * true, the property's event will not be fired. 80711 * @method setProperty 80712 * @param {String} key The name of the property 80713 * @param {String} value The value to set the property to 80714 * @param {Boolean} silent Whether the value should be set silently, 80715 * without firing the property event. 80716 * @return {Boolean} True, if the set was successful, false if it failed. 80717 */ 80718 setProperty: function (key, value, silent) { 80719 80720 var property; 80721 80722 key = key.toLowerCase(); 80723 YAHOO.log("setProperty: " + key + "=" + value, "info", "Config"); 80724 80725 if (this.queueInProgress && ! silent) { 80726 // Currently running through a queue... 80727 this.queueProperty(key,value); 80728 return true; 80729 80730 } else { 80731 property = this.config[key]; 80732 if (property && property.event) { 80733 if (property.validator && !property.validator(value)) { 80734 return false; 80735 } else { 80736 property.value = value; 80737 if (! silent) { 80738 this.fireEvent(key, value); 80739 this.configChangedEvent.fire([key, value]); 80740 } 80741 return true; 80742 } 80743 } else { 80744 return false; 80745 } 80746 } 80747 }, 80748 80749 /** 80750 * Sets the value of a property and queues its event to execute. If the 80751 * event is already scheduled to execute, it is 80752 * moved from its current position to the end of the queue. 80753 * @method queueProperty 80754 * @param {String} key The name of the property 80755 * @param {String} value The value to set the property to 80756 * @return {Boolean} true, if the set was successful, false if 80757 * it failed. 80758 */ 80759 queueProperty: function (key, value) { 80760 80761 key = key.toLowerCase(); 80762 YAHOO.log("queueProperty: " + key + "=" + value, "info", "Config"); 80763 80764 var property = this.config[key], 80765 foundDuplicate = false, 80766 iLen, 80767 queueItem, 80768 queueItemKey, 80769 queueItemValue, 80770 sLen, 80771 supercedesCheck, 80772 qLen, 80773 queueItemCheck, 80774 queueItemCheckKey, 80775 queueItemCheckValue, 80776 i, 80777 s, 80778 q; 80779 80780 if (property && property.event) { 80781 80782 if (!Lang.isUndefined(value) && property.validator && 80783 !property.validator(value)) { // validator 80784 return false;
80785 } else { 80786 80787 if (!Lang.isUndefined(value)) { 80788 property.value = value; 80789 } else { 80790 value = property.value; 80791 } 80792 80793 foundDuplicate = false; 80794 iLen = this.eventQueue.length; 80795 80796 for (i = 0; i < iLen; i++) { 80797 queueItem = this.eventQueue[i]; 80798 80799 if (queueItem) { 80800 queueItemKey = queueItem[0]; 80801 queueItemValue = queueItem[1]; 80802 80803 if (queueItemKey == key) { 80804 80805 /* 80806 found a dupe... push to end of queue, null 80807 current item, and break 80808 */ 80809 80810 this.eventQueue[i] = null; 80811 80812 this.eventQueue.push( 80813 [key, (!Lang.isUndefined(value) ? 80814 value : queueItemValue)]); 80815 80816 foundDuplicate = true; 80817 break; 80818 } 80819 } 80820 } 80821 80822 // this is a refire, or a new property in the queue 80823 80824 if (! foundDuplicate && !Lang.isUndefined(value)) { 80825 this.eventQueue.push([key, value]); 80826 } 80827 } 80828 80829 if (property.supercedes) { 80830 80831 sLen = property.supercedes.length; 80832 80833 for (s = 0; s < sLen; s++) { 80834 80835 supercedesCheck = property.supercedes[s]; 80836 qLen = this.eventQueue.length; 80837 80838 for (q = 0; q < qLen; q++) { 80839 queueItemCheck = this.eventQueue[q]; 80840 80841 if (queueItemCheck) { 80842 queueItemCheckKey = queueItemCheck[0]; 80843 queueItemCheckValue = queueItemCheck[1]; 80844 80845 if (queueItemCheckKey == 80846 supercedesCheck.toLowerCase() ) { 80847 80848 this.eventQueue.push([queueItemCheckKey, 80849 queueItemCheckValue]); 80850 80851 this.eventQueue[q] = null; 80852 break; 80853 80854 } 80855 } 80856 } 80857 } 80858 } 80859 80860 YAHOO.log("Config event queue: " + this.outputEventQueue(), "info", "Config"); 80861 80862 return true; 80863 } else { 80864 return false; 80865 } 80866 }, 80867 80868 /** 80869 * Fires the event for a property using the property's current value. 80870 * @method refireEvent 80871 * @param {String} key The name of the property 80872 */ 80873 refireEvent: function (key) { 80874 80875 key = key.toLowerCase(); 80876 80877 var property = this.config[key]; 80878 80879 if (property && property.event && 80880 80881 !Lang.isUndefined(property.value)) { 80882 80883 if (this.queueInProgress) { 80884 80885 this.queueProperty(key); 80886 80887 } else { 80888 80889 this.fireEvent(key, property.value); 80890 80891 } 80892 80893 } 80894 }, 80895 80896 /** 80897 * Applies a key-value Object literal to the configuration, replacing 80898 * any existing values, and queueing the property events. 80899 * Although the values will be set, fireQueue() must be called for their 80900 * associated events to execute. 80901 * @method applyConfig 80902 * @param {Object} userConfig The configuration Object literal 80903 * @param {Boolean} init When set to true, the initialConfig will 80904 * be set to the userConfig passed in, so that calling a reset will 80905 * reset the properties to the passed values. 80906 */ 80907 applyConfig: function (userConfig, init) { 80908 80909 var sKey, 80910 oConfig; 80911 80912 if (init) { 80913 oConfig = {}; 80914 for (sKey in userConfig) { 80915 if (Lang.hasOwnProperty(userConfig, sKey)) { 80916 oConfig[sKey.toLowerCase()] = userConfig[sKey]; 80917 } 80918 } 80919 this.initialConfig = oConfig; 80920 } 80921 80922 for (sKey in userConfig) { 80923 if (Lang.hasOwnProperty(userConfig, sKey)) { 80924 this.queueProperty(sKey, userConfig[sKey]); 80925 } 80926 } 80927 }, 80928 80929 /**
80930 * Refires the events for all configuration properties using their 80931 * current values. 80932 * @method refresh 80933 */ 80934 refresh: function () { 80935 80936 var prop; 80937 80938 for (prop in this.config) { 80939 if (Lang.hasOwnProperty(this.config, prop)) { 80940 this.refireEvent(prop); 80941 } 80942 } 80943 }, 80944 80945 /** 80946 * Fires the normalized list of queued property change events 80947 * @method fireQueue 80948 */ 80949 fireQueue: function () { 80950 80951 var i, 80952 queueItem, 80953 key, 80954 value, 80955 property; 80956 80957 this.queueInProgress = true; 80958 for (i = 0;i < this.eventQueue.length; i++) { 80959 queueItem = this.eventQueue[i]; 80960 if (queueItem) { 80961 80962 key = queueItem[0]; 80963 value = queueItem[1]; 80964 property = this.config[key]; 80965 80966 property.value = value; 80967 80968 // Clear out queue entry, to avoid it being 80969 // re-added to the queue by any queueProperty/supercedes 80970 // calls which are invoked during fireEvent 80971 this.eventQueue[i] = null; 80972 80973 this.fireEvent(key,value); 80974 } 80975 } 80976 80977 this.queueInProgress = false; 80978 this.eventQueue = []; 80979 }, 80980 80981 /** 80982 * Subscribes an external handler to the change event for any 80983 * given property. 80984 * @method subscribeToConfigEvent 80985 * @param {String} key The property name 80986 * @param {Function} handler The handler function to use subscribe to 80987 * the property's event 80988 * @param {Object} obj The Object to use for scoping the event handler 80989 * (see CustomEvent documentation) 80990 * @param {Boolean} overrideContext Optional. If true, will override 80991 * "this" within the handler to map to the scope Object passed into the 80992 * method. 80993 * @return {Boolean} True, if the subscription was successful, 80994 * otherwise false. 80995 */ 80996 subscribeToConfigEvent: function (key, handler, obj, overrideContext) { 80997 80998 var property = this.config[key.toLowerCase()]; 80999 81000 if (property && property.event) { 81001 if (!Config.alreadySubscribed(property.event, handler, obj)) { 81002 property.event.subscribe(handler, obj, overrideContext); 81003 } 81004 return true; 81005 } else { 81006 return false; 81007 } 81008 81009 }, 81010 81011 /** 81012 * Unsubscribes an external handler from the change event for any 81013 * given property. 81014 * @method unsubscribeFromConfigEvent 81015 * @param {String} key The property name 81016 * @param {Function} handler The handler function to use subscribe to 81017 * the property's event 81018 * @param {Object} obj The Object to use for scoping the event 81019 * handler (see CustomEvent documentation) 81020 * @return {Boolean} True, if the unsubscription was successful, 81021 * otherwise false. 81022 */ 81023 unsubscribeFromConfigEvent: function (key, handler, obj) { 81024 var property = this.config[key.toLowerCase()]; 81025 if (property && property.event) { 81026 return property.event.unsubscribe(handler, obj); 81027 } else { 81028 return false; 81029 } 81030 }, 81031 81032 /** 81033 * Returns a string representation of the Config object 81034 * @method toString 81035 * @return {String} The Config object in string format. 81036 */ 81037 toString: function () { 81038 var output = "Config"; 81039 if (this.owner) { 81040 output += " [" + this.owner.toString() + "]"; 81041 } 81042 return output; 81043 }, 81044 81045 /** 81046 * Returns a string representation of the Config object's current 81047 * CustomEvent queue 81048 * @method outputEventQueue 81049 * @return {String} The string list of CustomEvents currently queued 81050 * for execution 81051 */ 81052 outputEventQueue: function () { 81053 81054 var output = "", 81055 queueItem, 81056 q, 81057 nQueue = this.eventQueue.length; 81058 81059 for (q = 0; q < nQueue; q++) { 81060 queueItem = this.eventQueue[q]; 81061 if (queueItem) { 81062 output += queueItem[0] + "=" + queueItem[1] + ", "; 81063 } 81064 } 81065 return output; 81066 }, 81067 81068 /** 81069 * Sets all properties to null, unsubscribes all listeners from each 81070 * property's change event and all listeners from the configChangedEvent. 81071 * @method destroy 81072 */ 81073 destroy: function () { 81074 81075 var oConfig = this.config, 81076 sProperty, 81077 oProperty; 81078 81079 81080 for (sProperty in oConfig) { 81081 81082 if (Lang.hasOwnProperty(oConfig, sProperty)) { 81083 81084 oProperty = oConfig[sProperty]; 81085 81086 oProperty.event.unsubscribeAll(); 81087 oProperty.event = null; 81088 81089 } 81090 81091 } 81092 81093 this.configChangedEvent.unsubscribeAll(); 81094 81095 this.configChangedEvent = null; 81096 this.owner = null; 81097 this.config = null; 81098 this.initialConfig = null; 81099 this.eventQueue = null; 81100 81101 } 81102 81103 }; 81104 81105 81106 81107 /** 81108 * Checks to determine if a particular function/Object pair are already 81109 * subscribed to the specified CustomEvent 81110 * @method YAHOO.util.Config.alreadySubscribed 81111 * @static 81112 * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 81113 * the subscriptions 81114 * @param {Function} fn The function to look for in the subscribers list 81115 * @param {Object} obj The execution scope Object for the subscription 81116 * @return {Boolean} true, if the function/Object pair is already subscribed 81117 * to the CustomEvent passed in 81118 */ 81119 Config.alreadySubscribed = function (evt, fn, obj) { 81120 81121 var nSubscribers = evt.subscribers.length, 81122 subsc, 81123 i; 81124 81125 if (nSubscribers > 0) { 81126 i = nSubscribers - 1; 81127 do { 81128 subsc = evt.subscribers[i]; 81129 if (subsc && subsc.obj == obj && subsc.fn == fn) { 81130 return true; 81131 } 81132 } 81133 while (i--); 81134 } 81135 81136 return false;
81137 81138 }; 81139 81140 YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider); 81141 81142}()); 81143/** 81144* The datemath module provides utility methods for basic JavaScript Date object manipulation and 81145* comparison. 81146* 81147* @module datemath 81148*/ 81149 81150/** 81151* YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility 81152* used for adding, subtracting, and comparing dates. 81153* @namespace YAHOO.widget 81154* @class DateMath 81155*/ 81156YAHOO.widget.DateMath = { 81157 /** 81158 * Constant field representing Day 81159 * @property DAY 81160 * @static 81161 * @final 81162 * @type String 81163 */ 81164 DAY : "D", 81165 81166 /** 81167 * Constant field representing Week 81168 * @property WEEK 81169 * @static 81170 * @final 81171 * @type String 81172 */ 81173 WEEK : "W", 81174 81175 /** 81176 * Constant field representing Year 81177 * @property YEAR 81178 * @static 81179 * @final 81180 * @type String 81181 */ 81182 YEAR : "Y", 81183 81184 /** 81185 * Constant field representing Month 81186 * @property MONTH 81187 * @static 81188 * @final 81189 * @type String 81190 */ 81191 MONTH : "M", 81192 81193 /** 81194 * Constant field representing one day, in milliseconds 81195 * @property ONE_DAY_MS 81196 * @static 81197 * @final 81198 * @type Number 81199 */ 81200 ONE_DAY_MS : 1000*60*60*24, 81201 81202 /** 81203 * Constant field representing the date in first week of January 81204 * which identifies the first week of the year. 81205 * <p> 81206 * In the U.S, Jan 1st is normally used based on a Sunday start of week. 81207 * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week. 81208 * </p> 81209 * @property WEEK_ONE_JAN_DATE 81210 * @static 81211 * @type Number 81212 */ 81213 WEEK_ONE_JAN_DATE : 1, 81214 81215 /** 81216 * Adds the specified amount of time to the this instance. 81217 * @method add 81218 * @param {Date} date The JavaScript Date object to perform addition on 81219 * @param {String} field The field constant to be used for performing addition. 81220 * @param {Number} amount The number of units (measured in the field constant) to add to the date. 81221 * @return {Date} The resulting Date object 81222 */ 81223 add : function(date, field, amount) { 81224 var d = new Date(date.getTime()); 81225 switch (field) { 81226 case this.MONTH: 81227 var newMonth = date.getMonth() + amount; 81228 var years = 0; 81229 81230 if (newMonth < 0) { 81231 while (newMonth < 0) { 81232 newMonth += 12; 81233 years -= 1; 81234 } 81235 } else if (newMonth > 11) { 81236 while (newMonth > 11) { 81237 newMonth -= 12; 81238 years += 1; 81239 } 81240 } 81241 81242 d.setMonth(newMonth); 81243 d.setFullYear(date.getFullYear() + years); 81244 break; 81245 case this.DAY: 81246 this._addDays(d, amount); 81247 // d.setDate(date.getDate() + amount); 81248 break; 81249 case this.YEAR: 81250 d.setFullYear(date.getFullYear() + amount); 81251 break; 81252 case this.WEEK: 81253 this._addDays(d, (amount * 7)); 81254 // d.setDate(date.getDate() + (amount * 7)); 81255 break; 81256 } 81257 return d; 81258 }, 81259 81260 /** 81261 * Private helper method to account for bug in Safari 2 (webkit < 420) 81262 * when Date.setDate(n) is called with n less than -128 or greater than 127. 81263 * <p> 81264 * Fix approach and original findings are available here: 81265 * http://brianary.blogspot.com/2006/03/safari-date-bug.html 81266 * </p> 81267 * @method _addDays 81268 * @param {Date} d JavaScript date object 81269 * @param {Number} nDays The number of days to add to the date object (can be negative) 81270 * @private 81271 */ 81272 _addDays : function(d, nDays) { 81273 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) { 81274 if (nDays < 0) { 81275 // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127) 81276 for(var min = -128; nDays < min; nDays -= min) { 81277 d.setDate(d.getDate() + min); 81278 } 81279 } else { 81280 // Ensure we don't go above 96 + 31 = 127 81281 for(var max = 96;
81281 nDays > max; nDays -= max) { 81282 d.setDate(d.getDate() + max); 81283 } 81284 } 81285 // nDays should be remainder between -128 and 96 81286 } 81287 d.setDate(d.getDate() + nDays); 81288 }, 81289 81290 /** 81291 * Subtracts the specified amount of time from the this instance. 81292 * @method subtract 81293 * @param {Date} date The JavaScript Date object to perform subtraction on 81294 * @param {Number} field The this field constant to be used for performing subtraction. 81295 * @param {Number} amount The number of units (measured in the field constant) to subtract from the date. 81296 * @return {Date} The resulting Date object 81297 */ 81298 subtract : function(date, field, amount) { 81299 return this.add(date, field, (amount*-1)); 81300 }, 81301 81302 /** 81303 * Determines whether a given date is before another date on the calendar. 81304 * @method before 81305 * @param {Date} date The Date object to compare with the compare argument 81306 * @param {Date} compareTo The Date object to use for the comparison 81307 * @return {Boolean} true if the date occurs before the compared date; false if not. 81308 */ 81309 before : function(date, compareTo) { 81310 var ms = compareTo.getTime(); 81311 if (date.getTime() < ms) { 81312 return true; 81313 } else { 81314 return false; 81315 } 81316 }, 81317 81318 /** 81319 * Determines whether a given date is after another date on the calendar. 81320 * @method after 81321 * @param {Date} date The Date object to compare with the compare argument 81322 * @param {Date} compareTo The Date object to use for the comparison 81323 * @return {Boolean} true if the date occurs after the compared date; false if not. 81324 */ 81325 after : function(date, compareTo) { 81326 var ms = compareTo.getTime(); 81327 if (date.getTime() > ms) { 81328 return true; 81329 } else { 81330 return false; 81331 } 81332 }, 81333 81334 /** 81335 * Determines whether a given date is between two other dates on the calendar. 81336 * @method between 81337 * @param {Date} date The date to check for 81338 * @param {Date} dateBegin The start of the range 81339 * @param {Date} dateEnd The end of the range 81340 * @return {Boolean} true if the date occurs between the compared dates; false if not. 81341 */ 81342 between : function(date, dateBegin, dateEnd) { 81343 if (this.after(date, dateBegin) && this.before(date, dateEnd)) { 81344 return true; 81345 } else { 81346 return false; 81347 } 81348 }, 81349 81350 /** 81351 * Retrieves a JavaScript Date object representing January 1 of any given year. 81352 * @method getJan1 81353 * @param {Number} calendarYear The calendar year for which to retrieve January 1 81354 * @return {Date} January 1 of the calendar year specified. 81355 */ 81356 getJan1 : function(calendarYear) { 81357 return this.getDate(calendarYear,0,1); 81358 }, 81359 81360 /** 81361 * Calculates the number of days the specified date is from January 1 of the specified calendar year. 81362 * Passing January 1 to this function would return an offset value of zero. 81363 * @method getDayOffset 81364 * @param {Date} date The JavaScript date for which to find the offset 81365 * @param {Number} calendarYear The calendar year to use for determining the offset 81366 * @return {Number} The number of days since January 1 of the given year 81367 */ 81368 getDayOffset : function(date, calendarYear) { 81369 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1. 81370 81371 // Find the number of days the passed in date is away from the calendar year start 81372 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS); 81373 return dayOffset; 81374 }, 81375 81376 /** 81377 * Calculates the week number for the given date. Can currently support standard 81378 * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and 81379 * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year. 81380 * 81381 * @method getWeekNumber 81382 * @param {Date} date The JavaScript date for which to find the week number 81383 * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat). 81384 * Defaults to 0 81385 * @param {Number} janDate The date in the first week of January which defines week one for the year 81386 * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st). 81387 * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year. 81388 * 81389 * @return {Number} The number of the week containing the given date. 81390 */ 81391 getWeekNumber : function(date, firstDayOfWeek, janDate) { 81392 81393 // Setup Defaults 81394 firstDayOfWeek = firstDayOfWeek || 0; 81395 janDate = janDate || this.WEEK_ONE_JAN_DATE; 81396 81397 var targetDate = this.clearTime(date),
81398 startOfWeek, 81399 endOfWeek; 81400 81401 if (targetDate.getDay() === firstDayOfWeek) { 81402 startOfWeek = targetDate; 81403 } else { 81404 startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek); 81405 } 81406 81407 var startYear = startOfWeek.getFullYear(); 81408 81409 // DST shouldn't be a problem here, math is quicker than setDate(); 81410 endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS); 81411 81412 var weekNum; 81413 if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) { 81414 // If years don't match, endOfWeek is in Jan. and if the 81415 // week has WEEK_ONE_JAN_DATE in it, it's week one by definition. 81416 weekNum = 1; 81417 } else { 81418 // Get the 1st day of the 1st week, and 81419 // find how many days away we are from it. 81420 var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)), 81421 weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek); 81422 81423 // Round days to smoothen out 1 hr DST diff 81424 var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS); 81425 81426 // Calc. Full Weeks 81427 var rem = daysDiff % 7; 81428 var weeksDiff = (daysDiff - rem)/7; 81429 weekNum = weeksDiff + 1; 81430 } 81431 return weekNum; 81432 }, 81433 81434 /** 81435 * Get the first day of the week, for the give date. 81436 * @param {Date} dt The date in the week for which the first day is required. 81437 * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0) 81438 * @return {Date} The first day of the week 81439 */ 81440 getFirstDayOfWeek : function (dt, startOfWeek) { 81441 startOfWeek = startOfWeek || 0; 81442 var dayOfWeekIndex = dt.getDay(), 81443 dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7; 81444 81445 return this.subtract(dt, this.DAY, dayOfWeek); 81446 }, 81447 81448 /** 81449 * Determines if a given week overlaps two different years. 81450 * @method isYearOverlapWeek 81451 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week. 81452 * @return {Boolean} true if the date overlaps two different years. 81453 */ 81454 isYearOverlapWeek : function(weekBeginDate) { 81455 var overlaps = false; 81456 var nextWeek = this.add(weekBeginDate, this.DAY, 6); 81457 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) { 81458 overlaps = true; 81459 } 81460 return overlaps; 81461 }, 81462 81463 /** 81464 * Determines if a given week overlaps two different months. 81465 * @method isMonthOverlapWeek 81466 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week. 81467 * @return {Boolean} true if the date overlaps two different months. 81468 */ 81469 isMonthOverlapWeek : function(weekBeginDate) { 81470 var overlaps = false; 81471 var nextWeek = this.add(weekBeginDate, this.DAY, 6); 81472 if (nextWeek.getMonth() != weekBeginDate.getMonth()) { 81473 overlaps = true; 81474 } 81475 return overlaps; 81476 }, 81477 81478 /** 81479 * Gets the first day of a month containing a given date. 81480 * @method findMonthStart 81481 * @param {Date} date The JavaScript Date used to calculate the month start 81482 * @return {Date} The JavaScript Date representing the first day of the month 81483 */ 81484 findMonthStart : function(date) { 81485 var start = this.getDate(date.getFullYear(), date.getMonth(), 1); 81486 return start; 81487 }, 81488 81489 /** 81490 * Gets the last day of a month containing a given date. 81491 * @method findMonthEnd 81492 * @param {Date} date The JavaScript Date used to calculate the month end 81493 * @return {Date} The JavaScript Date representing the last day of the month 81494 */ 81495 findMonthEnd : function(date) { 81496 var start = this.findMonthStart(date); 81497 var nextMonth = this.add(start, this.MONTH, 1); 81498 var end = this.subtract(nextMonth, this.DAY, 1); 81499 return end; 81500 }, 81501 81502 /** 81503 * Clears the time fields from a given date, effectively setting the time to 12 noon. 81504 * @method clearTime 81505 * @param {Date} date The JavaScript Date for which the time fields will be cleared 81506 * @return {Date} The JavaScript Date cleared of all time fields 81507 */ 81508 clearTime : function(date) { 81509 date.setHours(12,0,0,0); 81510 return date; 81511 }, 81512 81513 /** 81514 * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object 81515 * are set to 0. The method allows Date instances to be created with the a year less than 100. "new Date(year, month, date)" implementations 81516 * set the year to 19xx if a year (xx) which is less than 100 is provided. 81517 * <p> 81518 * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure 81519 * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor. 81520 * </p> 81521 * @method getDate 81522 * @param {Number} y Year. 81523 * @param {Number} m Month index from 0 (Jan) to 11 (Dec). 81524 * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1. 81525 * @return {Date} The JavaScript date object with year, month, date set as provided. 81526 */ 81527 getDate : function(y, m, d) { 81528 var dt = null; 81529 if (YAHOO.lang.isUndefined(d)) { 81530 d = 1; 81531 } 81532 if (y >= 100) { 81533 dt = new Date(y, m, d); 81534 } else { 81535 dt = new Date(); 81536 dt.setFullYear(y); 81537 dt.setMonth(m); 81538 dt.setDate(d); 81539 dt.setHours(0,0,0,0); 81540 } 81541 return dt; 81542 } 81543}; 81544/** 81545* The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month or 81546* multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes. 81547* @module calendar 81548* @title Calendar 81549* @namespace YAHOO.widget 81550* @requires yahoo,dom,event 81551*/ 81552(function(){ 81553 81554 var Dom = YAHOO.util.Dom, 81555 Event = YAHOO.util.Event, 81556 Lang = YAHOO.lang, 81557 DateMath = YAHOO.widget.DateMath; 81558 81559/** 81560* Calendar is the base class for the Calendar widget. In its most basic 81561* implementation, it has the ability to render a calendar widget on the page 81562* that can be manipulated to select a single date, move back and forth between 81563* months and years. 81564* <p>To construct the placeholder for the calendar widget, the code is as 81565* follows: 81566* <xmp> 81567* <div id="calContainer"></div> 81568* </xmp> 81569* </p> 81570* <p> 81571* <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong> 81572* The Calendar can be constructed by simply providing a container ID string, 81573* or a reference to a container DIV HTMLElement (the element needs to exist 81574* in the document). 81575* 81576* E.g.: 81577* <xmp> 81578* var c = new YAHOO.widget.Calendar("calContainer", configOptions); 81579* </xmp> 81580* or: 81581* <xmp> 81582* var containerDiv = YAHOO.util.Dom.get("calContainer"); 81583* var c = new YAHOO.widget.Calendar(containerDiv, configOptions); 81584* </xmp> 81585* </p> 81586* <p> 81587* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix. 81588* For example if an ID is not provided, and the container's ID is "calContainer", the Calendar's ID will be set to "calContainer_t". 81589* </p> 81590* 81591* @namespace YAHOO.widget 81592* @class Calendar 81593* @constructor 81594* @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional. 81595* @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document. 81596* @param {Object}
81596 config optional The configuration object containing the initial configuration values for the Calendar. 81597*/ 81598function Calendar(id, containerId, config) { 81599 this.init.apply(this, arguments); 81600} 81601 81602/** 81603* The path to be used for images loaded for the Calendar 81604* @property YAHOO.widget.Calendar.IMG_ROOT 81605* @static 81606* @deprecated You can now customize images by overriding the calclose, calnavleft and calnavright default CSS classes for the close icon, left arrow and right arrow respectively 81607* @type String 81608*/ 81609Calendar.IMG_ROOT = null; 81610 81611/** 81612* Type constant used for renderers to represent an individual date (M/D/Y) 81613* @property YAHOO.widget.Calendar.DATE 81614* @static 81615* @final 81616* @type String 81617*/ 81618Calendar.DATE = "D"; 81619 81620/** 81621* Type constant used for renderers to represent an individual date across any year (M/D) 81622* @property YAHOO.widget.Calendar.MONTH_DAY 81623* @static 81624* @final 81625* @type String 81626*/ 81627Calendar.MONTH_DAY = "MD"; 81628 81629/** 81630* Type constant used for renderers to represent a weekday 81631* @property YAHOO.widget.Calendar.WEEKDAY 81632* @static 81633* @final 81634* @type String 81635*/ 81636Calendar.WEEKDAY = "WD"; 81637 81638/** 81639* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y) 81640* @property YAHOO.widget.Calendar.RANGE 81641* @static 81642* @final 81643* @type String 81644*/ 81645Calendar.RANGE = "R"; 81646 81647/** 81648* Type constant used for renderers to represent a month across any year 81649* @property YAHOO.widget.Calendar.MONTH 81650* @static 81651* @final 81652* @type String 81653*/ 81654Calendar.MONTH = "M"; 81655 81656/** 81657* Constant that represents the total number of date cells that are displayed in a given month 81658* @property YAHOO.widget.Calendar.DISPLAY_DAYS 81659* @static 81660* @final 81661* @type Number 81662*/ 81663Calendar.DISPLAY_DAYS = 42; 81664 81665/** 81666* Constant used for halting the execution of the remainder of the render stack 81667* @property YAHOO.widget.Calendar.STOP_RENDER 81668* @static 81669* @final 81670* @type String 81671*/ 81672Calendar.STOP_RENDER = "S"; 81673 81674/** 81675* Constant used to represent short date field string formats (e.g. Tu or Feb) 81676* @property YAHOO.widget.Calendar.SHORT 81677* @static 81678* @final 81679* @type String 81680*/ 81681Calendar.SHORT = "short"; 81682 81683/** 81684* Constant used to represent long date field string formats (e.g. Monday or February) 81685* @property YAHOO.widget.Calendar.LONG 81686* @static 81687* @final 81688* @type String 81689*/ 81690Calendar.LONG = "long"; 81691 81692/** 81693* Constant used to represent medium date field string formats (e.g. Mon) 81694* @property YAHOO.widget.Calendar.MEDIUM 81695* @static 81696* @final 81697* @type String 81698*/ 81699Calendar.MEDIUM = "medium"; 81700 81701/** 81702* Constant used to represent single character date field string formats (e.g. M, T, W) 81703* @property YAHOO.widget.Calendar.ONE_CHAR 81704* @static 81705* @final 81706* @type String 81707*/ 81708Calendar.ONE_CHAR = "1char"; 81709 81710/** 81711* The set of default Config property keys and values for the Calendar. 81712* 81713* <p> 81714* NOTE: This property is made public in order to allow users to change 81715* the default values of configuration properties. Users should not 81716* modify the key string, unless they are overriding the Calendar implementation 81717* </p> 81718* 81719* <p> 81720* The property is an object with key/value pairs, the key being the 81721* uppercase configuration property name and the value being an object 81722* literal with a key string property, and a value property, specifying the 81723* default value of the property. To override a default value, you can set 81724* the value property, for example, <code>YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;</code> 81725* 81726* @property YAHOO.widget.Calendar.DEFAULT_CONFIG 81727* @static 81728* @type Object 81729*/ 81730 81731Calendar.DEFAULT_CONFIG = { 81732 YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]}, 81733 TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]}, 81734 PAGEDATE : {key:"pagedate", value:null}, 81735 SELECTED : {key:"selected", value:[]}, 81736 TITLE : {key:"title", value:""}, 81737 CLOSE : {key:"close", value:false}, 81738 IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false}, 81739 MINDATE : {key:"mindate", value:null}, 81740 MAXDATE : {key:"maxdate", value:null}, 81741 MULTI_SELECT : {key:"multi_select", value:false}, 81742 OOM_SELECT : {key:"oom_select", value:false}, 81743 START_WEEKDAY : {key:"start_weekday", value:0}, 81744 SHOW_WEEKDAYS : {key:"show_weekdays", value:true}, 81745 SHOW_WEEK_HEADER : {key:"show_week_header", value:false}, 81746 SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false}, 81747 HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false}, 81748 NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} , 81749 NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} , 81750 MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]}, 81751 MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
81751November", "December"]}, 81752 WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]}, 81753 WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]}, 81754 WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]}, 81755 WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]}, 81756 LOCALE_MONTHS:{key:"locale_months", value:"long"}, 81757 LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"}, 81758 DATE_DELIMITER:{key:"date_delimiter", value:","}, 81759 DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"}, 81760 DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"}, 81761 MY_MONTH_POSITION:{key:"my_month_position", value:1}, 81762 MY_YEAR_POSITION:{key:"my_year_position", value:2}, 81763 MD_MONTH_POSITION:{key:"md_month_position", value:1}, 81764 MD_DAY_POSITION:{key:"md_day_position", value:2}, 81765 MDY_MONTH_POSITION:{key:"mdy_month_position", value:1}, 81766 MDY_DAY_POSITION:{key:"mdy_day_position", value:2}, 81767 MDY_YEAR_POSITION:{key:"mdy_year_position", value:3}, 81768 MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1}, 81769 MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2}, 81770 MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "}, 81771 MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""}, 81772 NAV: {key:"navigator", value: null}, 81773 STRINGS : { 81774 key:"strings", 81775 value: { 81776 previousMonth : "Previous Month", 81777 nextMonth : "Next Month", 81778 close: "Close" 81779 }, 81780 supercedes : ["close", "title"] 81781 } 81782}; 81783 81784/** 81785* The set of default Config property keys and values for the Calendar 81786* @property YAHOO.widget.Calendar._DEFAULT_CONFIG 81787* @deprecated Made public. See the public DEFAULT_CONFIG property for details 81788* @final 81789* @static 81790* @private 81791* @type Object 81792*/ 81793Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG; 81794 81795var DEF_CFG = Calendar.DEFAULT_CONFIG; 81796 81797/** 81798* The set of Custom Event types supported by the Calendar 81799* @property YAHOO.widget.Calendar._EVENT_TYPES 81800* @final 81801* @static 81802* @private 81803* @type Object 81804*/ 81805Calendar._EVENT_TYPES = { 81806 BEFORE_SELECT : "beforeSelect", 81807 SELECT : "select", 81808 BEFORE_DESELECT : "beforeDeselect", 81809 DESELECT : "deselect", 81810 CHANGE_PAGE : "changePage", 81811 BEFORE_RENDER : "beforeRender", 81812 RENDER : "render", 81813 BEFORE_DESTROY : "beforeDestroy", 81814 DESTROY : "destroy", 81815 RESET : "reset", 81816 CLEAR : "clear", 81817 BEFORE_HIDE : "beforeHide", 81818 HIDE : "hide", 81819 BEFORE_SHOW : "beforeShow", 81820 SHOW : "show", 81821 BEFORE_HIDE_NAV : "beforeHideNav", 81822 HIDE_NAV : "hideNav", 81823 BEFORE_SHOW_NAV : "beforeShowNav", 81824 SHOW_NAV : "showNav", 81825 BEFORE_RENDER_NAV : "beforeRenderNav", 81826 RENDER_NAV : "renderNav" 81827}; 81828 81829/** 81830* The set of default style constants for the Calendar 81831* @property YAHOO.widget.Calendar.STYLES 81832* @static 81833* @type Object An object with name/value pairs for the class name identifier/value. 81834*/ 81835Calendar.STYLES = { 81836 CSS_ROW_HEADER: "calrowhead", 81837 CSS_ROW_FOOTER: "calrowfoot", 81838 CSS_CELL : "calcell", 81839 CSS_CELL_SELECTOR : "selector", 81840 CSS_CELL_SELECTED : "selected", 81841 CSS_CELL_SELECTABLE : "selectable", 81842 CSS_CELL_RESTRICTED : "restricted", 81843 CSS_CELL_TODAY : "today", 81844 CSS_CELL_OOM : "oom", 81845 CSS_CELL_OOB : "previous", 81846 CSS_HEADER : "calheader", 81847 CSS_HEADER_TEXT : "calhead", 81848 CSS_BODY : "calbody", 81849 CSS_WEEKDAY_CELL : "calweekdaycell", 81850 CSS_WEEKDAY_ROW : "calweekdayrow", 81851 CSS_FOOTER : "calfoot", 81852 CSS_CALENDAR : "yui-calendar", 81853 CSS_SINGLE : "single", 81854 CSS_CONTAINER : "yui-calcontainer", 81855 CSS_NAV_LEFT : "calnavleft",
81856 CSS_NAV_RIGHT : "calnavright", 81857 CSS_NAV : "calnav", 81858 CSS_CLOSE : "calclose", 81859 CSS_CELL_TOP : "calcelltop", 81860 CSS_CELL_LEFT : "calcellleft", 81861 CSS_CELL_RIGHT : "calcellright", 81862 CSS_CELL_BOTTOM : "calcellbottom", 81863 CSS_CELL_HOVER : "calcellhover", 81864 CSS_CELL_HIGHLIGHT1 : "highlight1", 81865 CSS_CELL_HIGHLIGHT2 : "highlight2", 81866 CSS_CELL_HIGHLIGHT3 : "highlight3", 81867 CSS_CELL_HIGHLIGHT4 : "highlight4", 81868 CSS_WITH_TITLE: "withtitle", 81869 CSS_FIXED_SIZE: "fixedsize", 81870 CSS_LINK_CLOSE: "link-close" 81871}; 81872 81873/** 81874* The set of default style constants for the Calendar 81875* @property YAHOO.widget.Calendar._STYLES 81876* @deprecated Made public. See the public STYLES property for details 81877* @final 81878* @static 81879* @private 81880* @type Object 81881*/ 81882Calendar._STYLES = Calendar.STYLES; 81883 81884Calendar.prototype = { 81885 81886 /** 81887 * The configuration object used to set up the calendars various locale and style options. 81888 * @property Config 81889 * @private 81890 * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty. 81891 * @type Object 81892 */ 81893 Config : null, 81894 81895 /** 81896 * The parent CalendarGroup, only to be set explicitly by the parent group 81897 * @property parent 81898 * @type CalendarGroup 81899 */ 81900 parent : null, 81901 81902 /** 81903 * The index of this item in the parent group 81904 * @property index 81905 * @type Number 81906 */ 81907 index : -1, 81908 81909 /** 81910 * The collection of calendar table cells 81911 * @property cells 81912 * @type HTMLTableCellElement[] 81913 */ 81914 cells : null, 81915 81916 /** 81917 * The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D]. 81918 * @property cellDates 81919 * @type Array[](Number[]) 81920 */ 81921 cellDates : null, 81922 81923 /** 81924 * The id that uniquely identifies this Calendar. 81925 * @property id
81926 * @type String 81927 */ 81928 id : null, 81929 81930 /** 81931 * The unique id associated with the Calendar's container 81932 * @property containerId 81933 * @type String 81934 */ 81935 containerId: null, 81936 81937 /** 81938 * The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered. 81939 * @property oDomContainer 81940 * @type HTMLElement 81941 */ 81942 oDomContainer : null, 81943 81944 /** 81945 * A Date object representing today's date. 81946 * @deprecated Use the "today" configuration property 81947 * @property today 81948 * @type Date 81949 */ 81950 today : null, 81951 81952 /** 81953 * The list of render functions, along with required parameters, used to render cells. 81954 * @property renderStack 81955 * @type Array[] 81956 */ 81957 renderStack : null, 81958 81959 /** 81960 * A copy of the initial render functions created before rendering. 81961 * @property _renderStack 81962 * @private 81963 * @type Array 81964 */ 81965 _renderStack : null, 81966 81967 /** 81968 * A reference to the CalendarNavigator instance created for this Calendar. 81969 * Will be null if the "navigator" configuration property has not been set 81970 * @property oNavigator 81971 * @type CalendarNavigator 81972 */ 81973 oNavigator : null, 81974 81975 /** 81976 * The private list of initially selected dates. 81977 * @property _selectedDates 81978 * @private 81979 * @type Array 81980 */ 81981 _selectedDates : null, 81982 81983 /** 81984 * A map of DOM event handlers to attach to cells associated with specific CSS class names 81985 * @property domEventMap 81986 * @type Object 81987 */ 81988 domEventMap : null, 81989 81990 /** 81991 * Protected helper used to parse Calendar constructor/init arguments. 81992 * 81993 * As of 2.4.0, Calendar supports a simpler constructor 81994 * signature. This method reconciles arguments 81995 * received in the pre 2.4.0 and 2.4.0 formats. 81996 * 81997 * @protected 81998 * @method _parseArgs 81999 * @param {Array} Function "arguments" array 82000 * @return {Object} Object with id, container, config properties containing 82001 * the reconciled argument values. 82002 **/ 82003 _parseArgs : function(args) { 82004 /* 82005 2.4.0 Constructors signatures 82006 82007 new Calendar(String) 82008 new Calendar(HTMLElement) 82009 new Calendar(String, ConfigObject) 82010 new Calendar(HTMLElement, ConfigObject) 82011 82012 Pre 2.4.0 Constructor signatures 82013 82014 new Calendar(String, String) 82015 new Calendar(String, HTMLElement) 82016 new Calendar(String, String, ConfigObject) 82017 new Calendar(String, HTMLElement, ConfigObject) 82018 */ 82019 var nArgs = {id:null, container:null, config:null}; 82020 82021 if (args && args.length && args.length > 0) { 82022 switch (args.length) { 82023 case 1: 82024 nArgs.id = null; 82025 nArgs.container = args[0]; 82026 nArgs.config = null; 82027 break; 82028 case 2: 82029 if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) { 82030 nArgs.id = null; 82031 nArgs.container = args[0]; 82032 nArgs.config = args[1]; 82033 } else { 82034 nArgs.id = args[0]; 82035 nArgs.container = args[1]; 82036 nArgs.config = null; 82037 } 82038 break; 82039 default: // 3+ 82040 nArgs.id = args[0]; 82041 nArgs.container = args[1]; 82042 nArgs.config = args[2]; 82043 break; 82044 } 82045 } else { 82046 this.logger.log("Invalid constructor/init arguments", "error"); 82047 } 82048 return nArgs; 82049 }, 82050 82051 /** 82052 * Initializes the Calendar widget. 82053 * @method init 82054 * 82055 * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional. 82056 * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document. 82057 * @param {Object}
82057 config optional The configuration object containing the initial configuration values for the Calendar. 82058 */ 82059 init : function(id, container, config) { 82060 // Normalize 2.4.0, pre 2.4.0 args 82061 var nArgs = this._parseArgs(arguments); 82062 82063 id = nArgs.id; 82064 container = nArgs.container; 82065 config = nArgs.config; 82066 82067 this.oDomContainer = Dom.get(container); 82068 // Removing due to order of operations issue [ logger/id ]. 82069 // The log is kind of pointless because it'll barf on the next statement anyway. 82070 // Any code related changes are beyond the scope of 2.9.0 at this point 82071 // if (!this.oDomContainer) { this.logger.log("Container not found in document.", "error"); } 82072 82073 this._oDoc = this.oDomContainer.ownerDocument; 82074 82075 if (!this.oDomContainer.id) { 82076 this.oDomContainer.id = Dom.generateId(); 82077 } 82078 82079 if (!id) { 82080 id = this.oDomContainer.id + "_t"; 82081 } 82082 82083 this.id = id; 82084 this.containerId = this.oDomContainer.id; 82085 82086 this.logger = new YAHOO.widget.LogWriter("Calendar " + this.id); 82087 this.initEvents(); 82088 82089 /** 82090 * The Config object used to hold the configuration variables for the Calendar 82091 * @property cfg 82092 * @type YAHOO.util.Config 82093 */ 82094 this.cfg = new YAHOO.util.Config(this); 82095 82096 /** 82097 * The local object which contains the Calendar's options 82098 * @property Options 82099 * @type Object 82100 */ 82101 this.Options = {}; 82102 82103 /** 82104 * The local object which contains the Calendar's locale settings 82105 * @property Locale 82106 * @type Object 82107 */ 82108 this.Locale = {}; 82109 82110 this.initStyles(); 82111 82112 Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER); 82113 Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE); 82114 82115 this.cellDates = []; 82116 this.cells = []; 82117 this.renderStack = []; 82118 this._renderStack = []; 82119 82120 this.setupConfig(); 82121 82122 if (config) { 82123 this.cfg.applyConfig(config, true); 82124 } 82125 82126 this.cfg.fireQueue(); 82127 82128 this.today = this.cfg.getProperty("today"); 82129 }, 82130 82131 /** 82132 * Default Config listener for the iframe property. If the iframe config property is set to true, 82133 * renders the built-in IFRAME shim if the container is relatively or absolutely positioned. 82134 * 82135 * @method configIframe 82136 */ 82137 configIframe : function(type, args, obj) { 82138 var useIframe = args[0]; 82139 82140 if (!this.parent) { 82141 if (Dom.inDocument(this.oDomContainer)) { 82142 if (useIframe) { 82143 var pos = Dom.getStyle(this.oDomContainer, "position"); 82144 82145 if (pos == "absolute" || pos == "relative") { 82146 82147 if (!Dom.inDocument(this.iframe)) { 82148 this.iframe = document.createElement("iframe"); 82149 this.iframe.src = "javascript:false;"; 82150 82151 Dom.setStyle(this.iframe, "opacity", "0"); 82152 82153 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) { 82154 Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE); 82155 } 82156 82157 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild); 82158 } 82159 } 82160 } else { 82161 if (this.iframe) { 82162 if (this.iframe.parentNode) { 82163 this.iframe.parentNode.removeChild(this.iframe); 82164 } 82165 this.iframe = null; 82166 } 82167 } 82168 } 82169 } 82170 }, 82171 82172 /** 82173 * Default handler for the "title" property 82174 * @method configTitle 82175 */ 82176 configTitle : function(type, args, obj) { 82177 var title = args[0]; 82178 82179 // "" disables title bar 82180 if (title) { 82181 this.createTitleBar(title); 82182 } else { 82183 var close = this.cfg.getProperty(DEF_CFG.CLOSE.key); 82184 if (!close) { 82185 this.removeTitleBar(); 82186 } else { 82187 this.createTitleBar(" "); 82188 } 82189 } 82190 }, 82191 82192 /** 82193 * Default handler for the "close" property 82194 * @method configClose 82195 */ 82196 configClose : function(type, args, obj) { 82197 var close = args[0], 82198 title = this.cfg.getProperty(DEF_CFG.TITLE.key); 82199 82200 if (close) { 82201 if (!title) { 82202 this.createTitleBar(" "); 82203 } 82204 this.createCloseButton(); 82205 } else { 82206 this.removeCloseButton(); 82207 if (!title) { 82208 this.removeTitleBar(); 82209 } 82210 } 82211 }, 82212 82213 /** 82214 * Initializes Calendar's built-in CustomEvents 82215 * @method initEvents 82216 */ 82217 initEvents : function() { 82218 82219 var defEvents = Calendar._EVENT_TYPES, 82220 CE = YAHOO.util.CustomEvent, 82221 cal = this; // To help with minification 82222 82223 /** 82224 * Fired before a date selection is made 82225 * @event beforeSelectEvent 82226 */ 82227 cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT); 82228 82229 /** 82230 * Fired when a date selection is made 82231 * @event selectEvent 82232 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD]. 82233 */ 82234 cal.selectEvent = new CE(defEvents.SELECT); 82235 82236 /** 82237 * Fired before a date or set of dates is deselected 82238 * @event beforeDeselectEvent 82239 */ 82240 cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT); 82241 82242 /** 82243 * Fired when a date or set of dates is deselected 82244 * @event deselectEvent 82245 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD]. 82246 */ 82247 cal.deselectEvent = new CE(defEvents.DESELECT); 82248 82249 /** 82250 * Fired when the Calendar page is changed 82251 * @event changePageEvent 82252 * @param {Date} prevDate The date before the page was changed 82253 * @param {Date} newDate The date after the page was changed 82254 */ 82255 cal.changePageEvent = new CE(defEvents.CHANGE_PAGE); 82256 82257 /** 82258 * Fired before the Calendar is rendered 82259 * @event beforeRenderEvent 82260 */ 82261 cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER); 82262 82263 /** 82264 * Fired when the Calendar is rendered 82265 * @event renderEvent 82266 */ 82267 cal.renderEvent = new CE(defEvents.RENDER); 82268 82269 /** 82270 * Fired just before the Calendar is to be destroyed 82271 * @event beforeDestroyEvent 82272 */ 82273 cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY); 82274 82275 /** 82276 * Fired after the Calendar is destroyed. This event should be used 82277 * for notification only. When this event is fired, important Calendar instance 82278 * properties, dom references and event listeners have already been 82279 * removed/dereferenced, and hence the Calendar instance is not in a usable 82280 * state. 82281 * 82282 * @event destroyEvent 82283 */ 82284 cal.destroyEvent = new CE(defEvents.DESTROY); 82285 82286 /** 82287 * Fired when the Calendar is reset 82288 * @event resetEvent 82289 */ 82290 cal.resetEvent = new CE(defEvents.RESET); 82291 82292 /** 82293 * Fired when the Calendar is cleared 82294 * @event clearEvent 82295 */ 82296 cal.clearEvent = new CE(defEvents.CLEAR); 82297 82298 /** 82299 * Fired just before the Calendar is to be shown 82300 * @event beforeShowEvent 82301 */ 82302 cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW); 82303 82304 /** 82305 * Fired after the Calendar is shown 82306 * @event showEvent 82307 */ 82308 cal.showEvent = new CE(defEvents.SHOW); 82309 82310 /** 82311 * Fired just before the Calendar is to be hidden 82312 * @event beforeHideEvent 82313 */ 82314 cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE); 82315 82316 /** 82317 * Fired after the Calendar is hidden 82318 * @event hideEvent 82319 */ 82320 cal.hideEvent = new CE(defEvents.HIDE); 82321 82322 /** 82323 * Fired just before the CalendarNavigator is to be shown 82324 * @event beforeShowNavEvent 82325 */ 82326 cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV); 82327 82328 /** 82329 * Fired after the CalendarNavigator is shown 82330 * @event showNavEvent 82331 */ 82332 cal.showNavEvent = new CE(defEvents.SHOW_NAV); 82333 82334 /** 82335 * Fired just before the CalendarNavigator is to be hidden 82336 * @event beforeHideNavEvent 82337 */ 82338 cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV); 82339 82340 /** 82341 * Fired after the CalendarNavigator is hidden 82342 * @event hideNavEvent 82343 */ 82344 cal.hideNavEvent = new CE(defEvents.HIDE_NAV); 82345 82346 /** 82347 * Fired just before the CalendarNavigator is to be rendered 82348 * @event beforeRenderNavEvent 82349 */ 82350 cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV); 82351 82352 /** 82353 * Fired after the CalendarNavigator is rendered 82354 * @event renderNavEvent 82355 */ 82356 cal.renderNavEvent = new CE(defEvents.RENDER_NAV); 82357 82358 cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true); 82359 cal.selectEvent.subscribe(cal.onSelect, this, true); 82360 cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true); 82361 cal.deselectEvent.subscribe(cal.onDeselect, this, true); 82362 cal.changePageEvent.subscribe(cal.onChangePage, this, true); 82363 cal.renderEvent.subscribe(cal.onRender, this, true);
82364 cal.resetEvent.subscribe(cal.onReset, this, true); 82365 cal.clearEvent.subscribe(cal.onClear, this, true); 82366 }, 82367 82368 /** 82369 * The default event handler for clicks on the "Previous Month" navigation UI 82370 * 82371 * @method doPreviousMonthNav 82372 * @param {DOMEvent} e The DOM event 82373 * @param {Calendar} cal A reference to the calendar 82374 */ 82375 doPreviousMonthNav : function(e, cal) { 82376 Event.preventDefault(e); 82377 // previousMonth invoked in a timeout, to allow 82378 // event to bubble up, with correct target. Calling 82379 // previousMonth, will call render which will remove 82380 // HTML which generated the event, resulting in an 82381 // invalid event target in certain browsers. 82382 setTimeout(function() { 82383 cal.previousMonth(); 82384 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer); 82385 if (navs && navs[0]) { 82386 try { 82387 navs[0].focus(); 82388 } catch (ex) { 82389 // ignore 82390 } 82391 } 82392 }, 0); 82393 }, 82394 82395 /** 82396 * The default event handler for clicks on the "Next Month" navigation UI 82397 * 82398 * @method doNextMonthNav 82399 * @param {DOMEvent} e The DOM event 82400 * @param {Calendar} cal A reference to the calendar 82401 */ 82402 doNextMonthNav : function(e, cal) { 82403 Event.preventDefault(e); 82404 setTimeout(function() { 82405 cal.nextMonth(); 82406 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer); 82407 if (navs && navs[0]) { 82408 try { 82409 navs[0].focus(); 82410 } catch (ex) { 82411 // ignore 82412 } 82413 } 82414 }, 0); 82415 }, 82416 82417 /** 82418 * The default event handler for date cell selection. Currently attached to 82419 * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property. 82420 * 82421 * @method doSelectCell 82422 * @param {DOMEvent} e The DOM event 82423 * @param {Calendar} cal A reference to the calendar 82424 */ 82425 doSelectCell : function(e, cal) { 82426 var cell, d, date, index; 82427 82428 var target = Event.getTarget(e), 82429 tagName = target.tagName.toLowerCase(), 82430 defSelector = false; 82431 82432 while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) { 82433 82434 if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) { 82435 defSelector = true; 82436 } 82437 82438 target = target.parentNode; 82439 tagName = target.tagName.toLowerCase(); 82440 82441 if (target == this.oDomContainer || tagName == "html") { 82442 return; 82443 } 82444 } 82445 82446 if (defSelector) { 82447 // Stop link href navigation for default renderer 82448 Event.preventDefault(e); 82449 } 82450 82451 cell = target; 82452 82453 if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) { 82454 index = cal.getIndexFromId(cell.id); 82455 if (index > -1) { 82456 d = cal.cellDates[index]; 82457 if (d) { 82458 date = DateMath.getDate(d[0],d[1]-1,d[2]); 82459 82460 var link; 82461 82462 cal.logger.log("Selecting cell " + index + " via click", "info"); 82463 if (cal.Options.MULTI_SELECT) { 82464 link = cell.getElementsByTagName("a")[0]; 82465 if (link) { 82466 link.blur(); 82467 } 82468 82469 var cellDate = cal.cellDates[index]; 82470 var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate); 82471 82472 if (cellDateIndex > -1) { 82473 cal.deselectCell(index); 82474 } else { 82475 cal.selectCell(index); 82476 } 82477 82478 } else { 82479 link = cell.getElementsByTagName("a")[0]; 82480 if (link) { 82481 link.blur(); 82482 } 82483 cal.selectCell(index); 82484 } 82485 } 82486 } 82487 } 82488 }, 82489 82490 /** 82491 * The event that is executed when the user hovers over a cell 82492 * @method doCellMouseOver 82493 * @param {DOMEvent} e The event 82494 * @param {Calendar} cal A reference to the calendar passed by the Event utility 82495 */
82496 doCellMouseOver : function(e, cal) { 82497 var target; 82498 if (e) { 82499 target = Event.getTarget(e); 82500 } else { 82501 target = this; 82502 } 82503 82504 while (target.tagName && target.tagName.toLowerCase() != "td") { 82505 target = target.parentNode; 82506 if (!target.tagName || target.tagName.toLowerCase() == "html") { 82507 return; 82508 } 82509 } 82510 82511 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) { 82512 Dom.addClass(target, cal.Style.CSS_CELL_HOVER); 82513 } 82514 }, 82515 82516 /** 82517 * The event that is executed when the user moves the mouse out of a cell 82518 * @method doCellMouseOut 82519 * @param {DOMEvent} e The event 82520 * @param {Calendar} cal A reference to the calendar passed by the Event utility 82521 */ 82522 doCellMouseOut : function(e, cal) { 82523 var target; 82524 if (e) { 82525 target = Event.getTarget(e); 82526 } else { 82527 target = this; 82528 } 82529 82530 while (target.tagName && target.tagName.toLowerCase() != "td") { 82531 target = target.parentNode; 82532 if (!target.tagName || target.tagName.toLowerCase() == "html") { 82533 return; 82534 } 82535 } 82536 82537 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) { 82538 Dom.removeClass(target, cal.Style.CSS_CELL_HOVER); 82539 } 82540 }, 82541 82542 setupConfig : function() { 82543 82544 var cfg = this.cfg; 82545 82546 /** 82547 * The date to use to represent "Today". 82548 * 82549 * @config today 82550 * @type Date 82551 * @default The client side date (new Date()) when the Calendar is instantiated. 82552 */ 82553 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } ); 82554 82555 /** 82556 * The month/year representing the current visible Calendar date (mm/yyyy) 82557 * @config pagedate
82558 * @type String | Date 82559 * @default Today's date 82560 */ 82561 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } ); 82562 82563 /** 82564 * The date or range of dates representing the current Calendar selection 82565 * @config selected 82566 * @type String 82567 * @default [] 82568 */ 82569 cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } ); 82570 82571 /** 82572 * The title to display above the Calendar's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82573 * @config title 82574 * @type HTML 82575 * @default "" 82576 */ 82577 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } ); 82578 82579 /** 82580 * Whether or not a close button should be displayed for this Calendar 82581 * @config close 82582 * @type Boolean 82583 * @default false 82584 */ 82585 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } ); 82586 82587 /** 82588 * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below. 82589 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 82590 * enabled if required. 82591 * 82592 * @config iframe 82593 * @type Boolean 82594 * @default true for IE6 and below, false for all other browsers 82595 */ 82596 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } ); 82597 82598 /** 82599 * The minimum selectable date in the current Calendar (mm/dd/yyyy) 82600 * @config mindate
82601 * @type String | Date 82602 * @default null 82603 */ 82604 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } ); 82605 82606 /** 82607 * The maximum selectable date in the current Calendar (mm/dd/yyyy) 82608 * @config maxdate 82609 * @type String | Date 82610 * @default null 82611 */ 82612 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } ); 82613 82614 // Options properties 82615 82616 /** 82617 * True if the Calendar should allow multiple selections. False by default. 82618 * @config MULTI_SELECT 82619 * @type Boolean 82620 * @default false 82621 */ 82622 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82623 82624 /** 82625 * True if the Calendar should allow selection of out-of-month dates. False by default. 82626 * @config OOM_SELECT 82627 * @type Boolean 82628 * @default false 82629 */ 82630 cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82631 82632 /** 82633 * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6). 82634 * @config START_WEEKDAY 82635 * @type number 82636 * @default 0 82637 */ 82638 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber } ); 82639 82640 /** 82641 * True if the Calendar should show weekday labels. True by default. 82642 * @config SHOW_WEEKDAYS 82643 * @type Boolean 82644 * @default true 82645 */ 82646 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82647 82648 /** 82649 * True if the Calendar should show week row headers. False by default. 82650 * @config SHOW_WEEK_HEADER 82651 * @type Boolean 82652 * @default false 82653 */ 82654 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82655 82656 /** 82657 * True if the Calendar should show week row footers. False by default. 82658 * @config SHOW_WEEK_FOOTER 82659 * @type Boolean 82660 * @default false 82661 */ 82662 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82663 82664 /** 82665 * True if the Calendar should suppress weeks that are not a part of the current month. False by default. 82666 * @config HIDE_BLANK_WEEKS 82667 * @type Boolean 82668 * @default false 82669 */ 82670 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } ); 82671 82672 /** 82673 * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82674 * @config NAV_ARROW_LEFT
82675 * @type String 82676 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft" 82677 * @default null 82678 */ 82679 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } ); 82680 82681 /** 82682 * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82683 * @config NAV_ARROW_RIGHT 82684 * @type String 82685 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright" 82686 * @default null 82687 */ 82688 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } ); 82689 82690 // Locale properties 82691 82692 /** 82693 * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82694 * @config MONTHS_SHORT 82695 * @type HTML[] 82696 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 82697 */ 82698 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } ); 82699 82700 /** 82701 * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82702 * @config MONTHS_LONG 82703 * @type HTML[] 82704 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
82704November", "December" 82705 */ 82706 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } ); 82707 82708 /** 82709 * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82710 * @config WEEKDAYS_1CHAR 82711 * @type HTML[] 82712 * @default ["S", "M", "T", "W", "T", "F", "S"] 82713 */ 82714 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } ); 82715 82716 /** 82717 * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82718 * @config WEEKDAYS_SHORT 82719 * @type HTML[] 82720 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] 82721 */ 82722 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } ); 82723 82724 /** 82725 * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82726 * @config WEEKDAYS_MEDIUM 82727 * @type HTML[] 82728 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] 82729 */ 82730 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } ); 82731 82732 /** 82733 * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82734 * @config WEEKDAYS_LONG 82735 * @type HTML[] 82736 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] 82737 */ 82738 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } ); 82739 82740 /** 82741 * Refreshes the locale values used to build the Calendar. 82742 * @method refreshLocale 82743 * @private 82744 */ 82745 var refreshLocale = function() { 82746 cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key); 82747 cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key); 82748 }; 82749 82750 cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true); 82751 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true); 82752 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true); 82753 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true); 82754 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true); 82755 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true); 82756 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true); 82757 82758 /** 82759 * The setting that determines which length of month labels should be used. Possible values are "short" and "long". 82760 * @config LOCALE_MONTHS
82761 * @type String 82762 * @default "long" 82763 */ 82764 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } ); 82765 82766 /** 82767 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long". 82768 * @config LOCALE_WEEKDAYS 82769 * @type String 82770 * @default "short" 82771 */ 82772 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } ); 82773 82774 /** 82775 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to 82776 * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by 82777 * methods will always represent the Gregorian year, in order to maintain date/month/week values. 82778 * 82779 * @config YEAR_OFFSET 82780 * @type Number 82781 * @default 0 82782 */ 82783 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale } ); 82784 82785 /** 82786 * The value used to delimit individual dates in a date string passed to various Calendar functions. 82787 * @config DATE_DELIMITER
82788 * @type String 82789 * @default "," 82790 */ 82791 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } ); 82792 82793 /** 82794 * The value used to delimit date fields in a date string passed to various Calendar functions. 82795 * @config DATE_FIELD_DELIMITER 82796 * @type String 82797 * @default "/" 82798 */ 82799 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } ); 82800 82801 /** 82802 * The value used to delimit date ranges in a date string passed to various Calendar functions. 82803 * @config DATE_RANGE_DELIMITER 82804 * @type String 82805 * @default "-" 82806 */ 82807 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } ); 82808 82809 /** 82810 * The position of the month in a month/year date string 82811 * @config MY_MONTH_POSITION 82812 * @type Number 82813 * @default 1 82814 */ 82815 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82816 82817 /** 82818 * The position of the year in a month/year date string 82819 * @config MY_YEAR_POSITION 82820 * @type Number 82821 * @default 2 82822 */ 82823 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82824 82825 /** 82826 * The position of the month in a month/day date string 82827 * @config MD_MONTH_POSITION 82828 * @type Number 82829 * @default 1 82830 */ 82831 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82832 82833 /** 82834 * The position of the day in a month/year date string 82835 * @config MD_DAY_POSITION 82836 * @type Number 82837 * @default 2 82838 */ 82839 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82840 82841 /** 82842 * The position of the month in a month/day/year date string 82843 * @config MDY_MONTH_POSITION 82844 * @type Number 82845 * @default 1 82846 */ 82847 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82848 82849 /** 82850 * The position of the day in a month/day/year date string 82851 * @config MDY_DAY_POSITION 82852 * @type Number 82853 * @default 2 82854 */ 82855 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82856 82857 /** 82858 * The position of the year in a month/day/year date string 82859 * @config MDY_YEAR_POSITION 82860 * @type Number 82861 * @default 3 82862 */ 82863 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82864 82865 /** 82866 * The position of the month in the month year label string used as the Calendar header 82867 * @config MY_LABEL_MONTH_POSITION 82868 * @type Number 82869 * @default 1 82870 */ 82871 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82872 82873 /** 82874 * The position of the year in the month year label string used as the Calendar header 82875 * @config MY_LABEL_YEAR_POSITION 82876 * @type Number 82877 * @default 2 82878 */ 82879 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } ); 82880 82881 /** 82882 * The suffix used after the month when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82883 * @config MY_LABEL_MONTH_SUFFIX 82884 * @type HTML 82885 * @default " " 82886 */ 82887 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } ); 82888 82889 /** 82890 * The suffix used after the year when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82891 * @config MY_LABEL_YEAR_SUFFIX 82892 * @type HTML 82893 * @default "" 82894 */ 82895 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } ); 82896 82897 /** 82898 * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a 82899 * specific Month/Year without having to scroll sequentially through months. 82900 * <p> 82901 * Setting this property to null (default value) or false, will disable the CalendarNavigator UI. 82902 * </p> 82903 * <p> 82904 * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values. 82905 * </p> 82906 * <p> 82907 * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI. 82908 * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object. 82909 * Any properties which are not provided will use the default values (defined in the CalendarNavigator class). 82910 * </p> 82911 * <dl> 82912 * <dt>strings</dt> 82913 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 82914 * <dl> 82915 * <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</d
82915d> 82916 * <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd> 82917 * <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd> 82918 * <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd> 82919 * <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd> 82920 * </dl> 82921 * </dd> 82922 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd> 82923 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd> 82924 * </dl> 82925 * <p>E.g.</p> 82926 * <pre> 82927 * var navConfig = { 82928 * strings: { 82929 * month:"Calendar Month", 82930 * year:"Calendar Year", 82931 * submit: "Submit", 82932 * cancel: "Cancel", 82933 * invalidYear: "Please enter a valid year" 82934 * }, 82935 * monthFormat: YAHOO.widget.Calendar.SHORT, 82936 * initialFocus: "month" 82937 * } 82938 * </pre> 82939 * @config navigator 82940 * @type {Object|Boolean} 82941 * @default null 82942 */ 82943 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } ); 82944 82945 /** 82946 * The map of UI strings which the Calendar UI uses. 82947 * 82948 * @config strings 82949 * @type {Object} 82950 * @default An object with the properties shown below: 82951 * <dl> 82952 * <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd> 82953 * <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd> 82954 * <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd> 82955 * </dl> 82956 */ 82957 cfg.addProperty(DEF_CFG.STRINGS.key, { 82958 value:DEF_CFG.STRINGS.value, 82959 handler:this.configStrings, 82960 validator: function(val) { 82961 return Lang.isObject(val); 82962 }, 82963 supercedes:DEF_CFG.STRINGS.supercedes 82964 }); 82965 }, 82966 82967 /** 82968 * The default handler for the "strings" property 82969 * @method configStrings 82970 */
82971 configStrings : function(type, args, obj) { 82972 var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]); 82973 this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true); 82974 }, 82975 82976 /** 82977 * The default handler for the "pagedate" property 82978 * @method configPageDate 82979 */ 82980 configPageDate : function(type, args, obj) { 82981 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true); 82982 }, 82983 82984 /** 82985 * The default handler for the "mindate" property 82986 * @method configMinDate 82987 */ 82988 configMinDate : function(type, args, obj) { 82989 var val = args[0]; 82990 if (Lang.isString(val)) { 82991 val = this._parseDate(val); 82992 this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2])); 82993 } 82994 }, 82995 82996 /** 82997 * The default handler for the "maxdate" property 82998 * @method configMaxDate 82999 */ 83000 configMaxDate : function(type, args, obj) { 83001 var val = args[0]; 83002 if (Lang.isString(val)) { 83003 val = this._parseDate(val); 83004 this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2])); 83005 } 83006 }, 83007 83008 /** 83009 * The default handler for the "today" property 83010 * @method configToday 83011 */ 83012 configToday : function(type, args, obj) { 83013 // Only do this for initial set. Changing the today property after the initial 83014 // set, doesn't affect pagedate 83015 var val = args[0]; 83016 if (Lang.isString(val)) { 83017 val = this._parseDate(val); 83018 } 83019 var today = DateMath.clearTime(val); 83020 if (!this.cfg.initialConfig[DEF_CFG.PAGEDATE.key]) { 83021 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, today); 83022 } 83023 this.today = today; 83024 this.cfg.setProperty(DEF_CFG.TODAY.key, today, true); 83025 }, 83026 83027 /** 83028 * The default handler for the "selected" property 83029 * @method configSelected 83030 */ 83031 configSelected : function(type, args, obj) { 83032 var selected = args[0], 83033 cfgSelected = DEF_CFG.SELECTED.key; 83034 83035 if (selected) { 83036 if (Lang.isString(selected)) { 83037 this.cfg.setProperty(cfgSelected, this._parseDates(selected), true); 83038 } 83039 } 83040 if (! this._selectedDates) { 83041 this._selectedDates = this.cfg.getProperty(cfgSelected); 83042 } 83043 }, 83044 83045 /** 83046 * The default handler for all configuration options properties 83047 * @method configOptions 83048 */ 83049 configOptions : function(type, args, obj) { 83050 this.Options[type.toUpperCase()] = args[0]; 83051 }, 83052 83053 /** 83054 * The default handler for all configuration locale properties 83055 * @method configLocale 83056 */ 83057 configLocale : function(type, args, obj) { 83058 this.Locale[type.toUpperCase()] = args[0]; 83059 83060 this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key); 83061 this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key); 83062 }, 83063 83064 /** 83065 * The default handler for all configuration locale field length properties 83066 * @method configLocaleValues 83067 */ 83068 configLocaleValues : function(type, args, obj) { 83069 83070 type = type.toLowerCase(); 83071 83072 var val = args[0], 83073 cfg = this.cfg, 83074 Locale = this.Locale; 83075 83076 switch (type) { 83077 case DEF_CFG.LOCALE_MONTHS.key: 83078 switch (val) { 83079 case Calendar.SHORT: 83080 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat(); 83081 break; 83082 case Calendar.LONG: 83083 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat(); 83084 break; 83085 } 83086 break; 83087 case DEF_CFG.LOCALE_WEEKDAYS.key: 83088 switch (val) { 83089 case Calendar.ONE_CHAR: 83090 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat(); 83091 break; 83092 case Calendar.SHORT: 83093 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat(); 83094 break; 83095 case Calendar.MEDIUM: 83096 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat(); 83097 break; 83098 case Calendar.LONG: 83099 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat(); 83100 break; 83101 } 83102 83103 var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key); 83104 83105 if (START_WEEKDAY > 0) { 83106 for (var w=0; w < START_WEEKDAY; ++w) { 83107 Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift()); 83108 } 83109 } 83110 break; 83111 } 83112 }, 83113 83114 /** 83115 * The default handler for the "navigator" property 83116 * @method configNavigator 83117 */ 83118 configNavigator : function(type, args, obj) { 83119 var val = args[0]; 83120 if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) { 83121 if (!this.oNavigator) { 83122 this.oNavigator = new YAHOO.widget.CalendarNavigator(this); 83123 // Cleanup DOM Refs/Events before innerHTML is removed. 83124 this.beforeRenderEvent.subscribe(function () { 83125 if (!this.pages) { 83126 this.oNavigator.erase(); 83127 } 83128 }, this, true); 83129 } 83130 } else { 83131 if (this.oNavigator) { 83132 this.oNavigator.destroy(); 83133 this.oNavigator = null; 83134 } 83135 } 83136 }, 83137 83138 /** 83139 * Defines the class names used by Calendar when rendering to DOM. NOTE: The class names are added to the DOM as HTML and should be escaped by the implementor if coming from an external source. 83140 * @method initStyles 83141 */ 83142 initStyles : function() { 83143 83144 var defStyle = Calendar.STYLES; 83145 83146 this.Style = { 83147 /** 83148 * @property Style.CSS_ROW_HEADER 83149 */ 83150 CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER, 83151 /** 83152 * @property Style.CSS_ROW_FOOTER 83153 */ 83154 CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER, 83155 /** 83156 * @property Style.CSS_CELL 83157 */ 83158 CSS_CELL : defStyle.CSS_CELL, 83159 /** 83160 * @property Style.CSS_CELL_SELECTOR 83161 */ 83162 CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR, 83163 /** 83164 * @property Style.CSS_CELL_SELECTED 83165 */ 83166 CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED, 83167 /** 83168 * @property Style.CSS_CELL_SELECTABLE 83169 */ 83170 CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE, 83171 /** 83172 * @property Style.CSS_CELL_RESTRICTED 83173 */ 83174 CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED, 83175 /** 83176 * @property Style.CSS_CELL_TODAY 83177 */ 83178 CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY, 83179 /** 83180 * @property Style.CSS_CELL_OOM 83181 */ 83182 CSS_CELL_OOM : defStyle.CSS_CELL_OOM, 83183 /** 83184 * @property Style.CSS_CELL_OOB 83185 */ 83186 CSS_CELL_OOB : defStyle.CSS_CELL_OOB, 83187 /** 83188 * @property Style.CSS_HEADER 83189 */ 83190 CSS_HEADER : defStyle.CSS_HEADER, 83191 /** 83192 * @property Style.CSS_HEADER_TEXT 83193 */ 83194 CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT, 83195 /** 83196 * @property Style.CSS_BODY 83197 */ 83198 CSS_BODY : defStyle.CSS_BODY, 83199 /** 83200 * @property Style.CSS_WEEKDAY_CELL 83201 */ 83202 CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL, 83203 /** 83204 * @property Style.CSS_WEEKDAY_ROW 83205 */ 83206 CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW, 83207 /** 83208 * @property Style.CSS_FOOTER 83209 */ 83210 CSS_FOOTER : defStyle.CSS_FOOTER, 83211 /** 83212 * @property Style.CSS_CALENDAR 83213 */ 83214 CSS_CALENDAR : defStyle.CSS_CALENDAR, 83215 /** 83216 * @property Style.CSS_SINGLE 83217 */ 83218 CSS_SINGLE : defStyle.CSS_SINGLE, 83219 /** 83220 * @property Style.CSS_CONTAINER 83221 */ 83222 CSS_CONTAINER : defStyle.CSS_CONTAINER, 83223 /** 83224 * @property Style.CSS_NAV_LEFT 83225 */ 83226 CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT, 83227 /** 83228 * @property Style.CSS_NAV_RIGHT 83229 */ 83230 CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT, 83231 /** 83232 * @property Style.CSS_NAV 83233 */ 83234 CSS_NAV : defStyle.CSS_NAV, 83235 /** 83236 * @property Style.CSS_CLOSE 83237 */ 83238 CSS_CLOSE : defStyle.CSS_CLOSE, 83239 /** 83240 * @property Style.CSS_CELL_TOP 83241 */ 83242 CSS_CELL_TOP : defStyle.CSS_CELL_TOP, 83243 /** 83244 * @property Style.CSS_CELL_LEFT 83245 */ 83246 CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT, 83247 /** 83248 * @property Style.CSS_CELL_RIGHT 83249 */ 83250 CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT, 83251 /** 83252 * @property Style.CSS_CELL_BOTTOM 83253 */ 83254 CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM, 83255 /** 83256 * @property Style.CSS_CELL_HOVER 83257 */ 83258 CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER, 83259 /** 83260 * @property Style.CSS_CELL_HIGHLIGHT1 83261 */ 83262 CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1, 83263 /** 83264 * @property Style.CSS_CELL_HIGHLIGHT2 83265 */ 83266 CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2, 83267 /** 83268 * @property Style.CSS_CELL_HIGHLIGHT3 83269 */ 83270 CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3, 83271 /** 83272 * @property Style.CSS_CELL_HIGHLIGHT4 83273 */ 83274 CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4, 83275 /** 83276 * @property Style.CSS_WITH_TITLE 83277 */ 83278 CSS_WITH_TITLE : defStyle.CSS_WITH_TITLE, 83279 /** 83280 * @property Style.CSS_FIXED_SIZE 83281 */ 83282 CSS_FIXED_SIZE : defStyle.CSS_FIXED_SIZE, 83283 /** 83284 * @property Style.CSS_LINK_CLOSE 83285 */ 83286 CSS_LINK_CLOSE : defStyle.CSS_LINK_CLOSE 83287 }; 83288 }, 83289 83290 /** 83291 * Builds the date label that will be displayed in the calendar header or 83292 * footer, depending on configuration. 83293 * @method buildMonthLabel 83294 * @return {HTML} The formatted calendar month label 83295 */ 83296 buildMonthLabel : function() { 83297 return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key)); 83298 }, 83299 83300 /** 83301 * Helper method, to format a Month Year string, given a JavaScript Date, based on the 83302 * Calendar localization settings 83303 * 83304 * @method _buildMonthLabel 83305 * @private 83306 * @param {Date} date 83307 * @return {HTML} Formated month, year string 83308 */ 83309 _buildMonthLabel : function(date) { 83310 var monthLabel = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX, 83311 yearLabel = (date.getFullYear() + this.Locale.YEAR_OFFSET) + this.Locale.MY_LABEL_YEAR_SUFFIX; 83312 83313 if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) { 83314 return yearLabel + monthLabel; 83315 } else { 83316 return monthLabel + yearLabel; 83317 } 83318 }, 83319 83320 /** 83321 * Builds the date digit that will be displayed in calendar cells 83322 * @method buildDayLabel 83323 * @param {Date} workingDate The current working date 83324 * @return {Number} The day 83325 */ 83326 buildDayLabel : function(workingDate) { 83327 return workingDate.getDate(); 83328 }, 83329 83330 /** 83331 * Creates the title bar element and adds it to Calendar container DIV. NOTE: The title parameter passed into this method is added to the DOM as HTML and should be escaped by the implementor if coming from an external source. 83332 * 83333 * @method createTitleBar 83334 * @param {HTML} strTitle The title to display in the title bar 83335 * @return The title bar element 83336 */ 83337 createTitleBar : function(strTitle) { 83338 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div"); 83339 tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE; 83340 tDiv.innerHTML = strTitle; 83341 this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild); 83342 83343 Dom.addClass(this.oDomContainer, this.Style.CSS_WITH_TITLE); 83344 83345 return tDiv; 83346 }, 83347 83348 /** 83349 * Removes the title bar element from the DOM 83350 * 83351 * @method removeTitleBar 83352 */ 83353 removeTitleBar : function() { 83354 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null; 83355 if (tDiv) { 83356 Event.purgeElement(tDiv); 83357 this.oDomContainer.removeChild(tDiv); 83358 } 83359 Dom.removeClass(this.oDomContainer, this.Style.CSS_WITH_TITLE); 83360 }, 83361 83362 /** 83363 * Creates the close button HTML element and adds it to Calendar container DIV 83364 * 83365 * @method createCloseButton 83366 * @return {HTMLElement} The close HTML element created 83367 */ 83368 createCloseButton : function() { 83369 var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE, 83370 cssLinkClose = this.Style.CSS_LINK_CLOSE, 83371 DEPR_CLOSE_PATH = "us/my/bn/x_d.gif", 83372 83373 lnk = Dom.getElementsByClassName(cssLinkClose, "a", this.oDomContainer)[0],
83374 strings = this.cfg.getProperty(DEF_CFG.STRINGS.key), 83375 closeStr = (strings && strings.close) ? strings.close : ""; 83376 83377 if (!lnk) { 83378 lnk = document.createElement("a"); 83379 Event.addListener(lnk, "click", function(e, cal) { 83380 cal.hide(); 83381 Event.preventDefault(e); 83382 }, this); 83383 } 83384 83385 lnk.href = "#"; 83386 lnk.className = cssLinkClose; 83387 83388 if (Calendar.IMG_ROOT !== null) { 83389 var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img"); 83390 img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH; 83391 img.className = cssClose; 83392 lnk.appendChild(img); 83393 } else { 83394 lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>'; 83395 } 83396 this.oDomContainer.appendChild(lnk); 83397 83398 return lnk; 83399 }, 83400 83401 /** 83402 * Removes the close button HTML element from the DOM 83403 * 83404 * @method removeCloseButton 83405 */ 83406 removeCloseButton : function() { 83407 var btn = Dom.getElementsByClassName(this.Style.CSS_LINK_CLOSE, "a", this.oDomContainer)[0] || null; 83408 if (btn) { 83409 Event.purgeElement(btn); 83410 this.oDomContainer.removeChild(btn); 83411 } 83412 }, 83413 83414 /** 83415 * Renders the calendar header. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 83416 * @method renderHeader 83417 * @param {HTML[]} html The current working HTML array 83418 * @return {HTML[]} The current working HTML array 83419 */ 83420 renderHeader : function(html) { 83421 83422 this.logger.log("Rendering header", "render"); 83423 83424 var colSpan = 7, 83425 DEPR_NAV_LEFT = "us/tr/callt.gif", 83426 DEPR_NAV_RIGHT = "us/tr/calrt.gif", 83427 cfg = this.cfg, 83428 pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key), 83429 strings= cfg.getProperty(DEF_CFG.STRINGS.key), 83430 prevStr = (strings && strings.previousMonth) ? strings.previousMonth : "", 83431 nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "", 83432 monthLabel; 83433 83434 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) { 83435 colSpan += 1; 83436 } 83437 83438 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) { 83439 colSpan += 1; 83440 } 83441 83442 html[html.length] = "<thead>"; 83443 html[html.length] = "<tr>"; 83444 html[html.length] = '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">'; 83445 html[html.length] = '<div class="' + this.Style.CSS_HEADER + '">'; 83446 83447 var renderLeft, renderRight = false; 83448 83449 if (this.parent) { 83450 if (this.index === 0) { 83451 renderLeft = true; 83452 } 83453 if (this.index == (this.parent.cfg.getProperty("pages") -1)) { 83454 renderRight = true; 83455 } 83456 } else { 83457 renderLeft = true; 83458 renderRight = true; 83459 } 83460 83461 if (renderLeft) { 83462 monthLabel = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1)); 83463 83464 var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key); 83465 // Check for deprecated customization - If someone set IMG_ROOT, but didn't set NAV_ARROW_LEFT, then set NAV_ARROW_LEFT to the old deprecated value 83466 if (leftArrow === null && Calendar.IMG_ROOT !== null) { 83467 leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT; 83468 } 83469 var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"'; 83470 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>'; 83471 } 83472 83473 var lbl = this.buildMonthLabel(); 83474 var cal = this.parent || this; 83475 if (cal.cfg.getProperty("navigator")) { 83476 lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>"; 83477 } 83478 html[html.length] = lbl; 83479 83480 if (renderRight) { 83481 monthLabel = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1)); 83482 83483 var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key); 83484 if (rightArrow === null && Calendar.IMG_ROOT !== null) { 83485 rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT; 83486 } 83487 var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"'; 83488 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">
83488' + nextStr + ' (' + monthLabel + ')' + '</a>'; 83489 } 83490 83491 html[html.length] = '</div>\n</th>\n</tr>'; 83492 83493 if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) { 83494 html = this.buildWeekdays(html); 83495 } 83496 83497 html[html.length] = '</thead>'; 83498 83499 return html; 83500 }, 83501 83502 /** 83503 * Renders the Calendar's weekday headers. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 83504 * @method buildWeekdays 83505 * @param {HTML[]} html The current working HTML array 83506 * @return {HTML[]} The current working HTML array 83507 */ 83508 buildWeekdays : function(html) { 83509 83510 html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">'; 83511 83512 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) { 83513 html[html.length] = '<th> </th>'; 83514 } 83515 83516 for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) { 83517 html[html.length] = '<th class="' + this.Style.CSS_WEEKDAY_CELL + '">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>'; 83518 } 83519 83520 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) { 83521 html[html.length] = '<th> </th>'; 83522 } 83523 83524 html[html.length] = '</tr>'; 83525 83526 return html; 83527 }, 83528 83529 /** 83530 * Renders the calendar body. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 83531 * @method renderBody 83532 * @param {Date} workingDate The current working Date being used for the render process 83533 * @param {HTML[]} html The current working HTML array 83534 * @return {HTML[]} The current working HTML array 83535 */ 83536 renderBody : function(workingDate, html) { 83537 this.logger.log("Rendering body", "render"); 83538 83539 var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key); 83540 83541 this.preMonthDays = workingDate.getDay(); 83542 if (startDay > 0) { 83543 this.preMonthDays -= startDay; 83544 } 83545 if (this.preMonthDays < 0) { 83546 this.preMonthDays += 7; 83547 } 83548 83549 this.monthDays = DateMath.findMonthEnd(workingDate).getDate(); 83550 this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays; 83551 83552 this.logger.log(this.preMonthDays + " preciding out-of-month days", "render"); 83553 this.logger.log(this.monthDays + " month days", "render"); 83554 this.logger.log(this.postMonthDays + " post-month days", "render"); 83555 83556 workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays); 83557 this.logger.log("Calendar page starts on " + workingDate, "render"); 83558 83559 var weekNum, 83560 weekClass, 83561 weekPrefix = "w", 83562 cellPrefix = "_cell", 83563 workingDayPrefix = "wd", 83564 dayPrefix = "d", 83565 cellRenderers, 83566 renderer, 83567 t = this.today, 83568 cfg = this.cfg, 83569 oom, 83570 todayYear = t.getFullYear(), 83571 todayMonth = t.getMonth(), 83572 todayDate = t.getDate(), 83573 useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key), 83574 hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key), 83575 showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key), 83576 showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key), 83577 oomSelect = cfg.getProperty(DEF_CFG.OOM_SELECT.key), 83578 mindate = cfg.getProperty(DEF_CFG.MINDATE.key), 83579 maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key), 83580 yearOffset = this.Locale.YEAR_OFFSET; 83581 83582 if (mindate) { 83583 mindate = DateMath.clearTime(mindate); 83584 } 83585 if (maxdate) { 83586 maxdate = DateMath.clearTime(maxdate); 83587 } 83588 83589 html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">'; 83590 83591 var i = 0, 83592 tempDiv = document.createElement("div"), 83593 cell = document.createElement("td"); 83594 83595 tempDiv.appendChild(cell); 83596 83597 var cal = this.parent || this; 83598 83599 for (var r = 0; r < 6; r++) { 83600 weekNum = DateMath.getWeekNumber(workingDate, startDay); 83601 weekClass = weekPrefix + weekNum; 83602 83603 // Local OOM check for performance, since we already have pagedate 83604 if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) { 83605 break; 83606 } else { 83607 html[html.length] = '<tr class="' + weekClass + '">'; 83608 83609 if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); } 83610 83611 for (var d=0; d < 7; d++){ // Render actual days 83612 83613 cellRenderers = []; 83614 83615 this.clearElement(cell); 83616 cell.className = this.Style.CSS_CELL; 83617 cell.id = this.id + cellPrefix + i; 83618 this.logger.log("Rendering cell " + cell.id + " (" + workingDate.getFullYear() + yearOffset + "-" + (workingDate.getMonth()+1) + "-" + workingDate.getDate() + ")", "cellrender"); 83619 83620 if (workingDate.getDate() == todayDate && 83621 workingDate.getMonth() == todayMonth && 83622 workingDate.getFullYear() == todayYear) { 83623 cellRenderers[cellRenderers.length]=cal.renderCellStyleToday; 83624 } 83625 83626 var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]; 83627 this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates 83628 83629 // Local OOM check for performance, since we already have pagedate 83630 oom = workingDate.getMonth() != useDate.getMonth(); 83631 if (oom && !oomSelect) { 83632 cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth; 83633 } else { 83634 Dom.addClass(cell, workingDayPrefix + workingDate.getDay()); 83635 Dom.addClass(cell, dayPrefix + workingDate.getDate()); 83636 83637 // Concat, so that we're not splicing from an array 83638 // which we're also iterating 83639 var rs = this.renderStack.concat(); 83640 83641 for (var s=0, l = rs.length; s < l; ++s) { 83642 83643 renderer = null; 83644 83645 var rArray = rs[s], 83646 type = rArray[0], 83647 month, 83648 day, 83649 year; 83650 83651 switch (type) { 83652 case Calendar.DATE:
83653 month = rArray[1][1]; 83654 day = rArray[1][2]; 83655 year = rArray[1][0]; 83656 83657 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) { 83658 renderer = rArray[2]; 83659 this.renderStack.splice(s,1); 83660 } 83661 83662 break; 83663 case Calendar.MONTH_DAY: 83664 month = rArray[1][0]; 83665 day = rArray[1][1]; 83666 83667 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) { 83668 renderer = rArray[2]; 83669 this.renderStack.splice(s,1); 83670 } 83671 break; 83672 case Calendar.RANGE: 83673 var date1 = rArray[1][0], 83674 date2 = rArray[1][1], 83675 d1month = date1[1], 83676 d1day = date1[2], 83677 d1year = date1[0], 83678 d1 = DateMath.getDate(d1year, d1month-1, d1day), 83679 d2month = date2[1], 83680 d2day = date2[2], 83681 d2year = date2[0], 83682 d2 = DateMath.getDate(d2year, d2month-1, d2day); 83683 83684 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) { 83685 renderer = rArray[2]; 83686 83687 if (workingDate.getTime()==d2.getTime()) { 83688 this.renderStack.splice(s,1); 83689 } 83690 } 83691 break; 83692 case Calendar.WEEKDAY: 83693 var weekday = rArray[1][0]; 83694 if (workingDate.getDay()+1 == weekday) { 83695 renderer = rArray[2]; 83696 } 83697 break; 83698 case Calendar.MONTH: 83699 month = rArray[1][0]; 83700 if (workingDate.getMonth()+1 == month) { 83701 renderer = rArray[2]; 83702 } 83703 break; 83704 } 83705 83706 if (renderer) { 83707 cellRenderers[cellRenderers.length]=renderer; 83708 } 83709 } 83710 83711 } 83712 83713 if (this._indexOfSelectedFieldArray(workingArray) > -1) { 83714 cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected; 83715 } 83716 83717 if (oom) { 83718 cellRenderers[cellRenderers.length] = cal.styleCellNotThisMonth; 83719 } 83720 83721 if ((mindate && (workingDate.getTime() < mindate.getTime())) || (maxdate && (workingDate.getTime() > maxdate.getTime()))) { 83722 cellRenderers[cellRenderers.length] = cal.renderOutOfBoundsDate; 83723 } else { 83724 cellRenderers[cellRenderers.length] = cal.styleCellDefault; 83725 cellRenderers[cellRenderers.length] = cal.renderCellDefault; 83726 } 83727 83728 for (var x=0; x < cellRenderers.length; ++x) { 83729 this.logger.log("renderer[" + x + "] for (" + workingDate.getFullYear() + yearOffset + "-" + (workingDate.getMonth()+1) + "-" + workingDate.getDate() + ")", "cellrender"); 83730 if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) { 83731 break; 83732 } 83733 } 83734 83735 workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS); 83736 // Just in case we crossed DST/Summertime boundaries 83737 workingDate = DateMath.clearTime(workingDate); 83738 83739 if (i >= 0 && i <= 6) { 83740 Dom.addClass(cell, this.Style.CSS_CELL_TOP); 83741 } 83742 if ((i % 7) === 0) { 83743 Dom.addClass(cell, this.Style.CSS_CELL_LEFT); 83744 } 83745 if (((i+1) % 7) === 0) { 83746 Dom.addClass(cell, this.Style.CSS_CELL_RIGHT); 83747 } 83748 83749 var postDays = this.postMonthDays; 83750 if (hideBlankWeeks && postDays >= 7) { 83751 var blankWeeks = Math.floor(postDays/7); 83752 for (var p=0;p<blankWeeks;++p) { 83753 postDays -= 7; 83754 } 83755 } 83756 83757 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) { 83758 Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM); 83759 } 83760 83761 html[html.length] = tempDiv.innerHTML; 83762 i++; 83763 } 83764 83765 if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); } 83766 83767 html[html.length] = '</tr>'; 83768 } 83769 } 83770 83771 html[html.length] = '</tbody>'; 83772 83773 return html; 83774 }, 83775 83776 /** 83777 * Renders the calendar footer. In the default implementation, there is no footer. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 83778 * @method renderFooter 83779 * @param {HTML[]} html The current working HTML array 83780 * @return {HTML[]} The current working HTML array 83781 */ 83782 renderFooter : function(html) { return html; }, 83783 83784 /** 83785 * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute 83786 * when the method is called: renderHeader, renderBody, renderFooter. 83787 * Refer to the documentation for those methods for information on individual render tasks. 83788 * @method render 83789 */ 83790 render : function() { 83791 this.beforeRenderEvent.fire(); 83792 83793 // Find starting day of the current month 83794 var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key)); 83795 83796 this.resetRenderers(); 83797 this.cellDates.length = 0; 83798 83799 Event.purgeElement(this.oDomContainer, true); 83800 83801 var html = [], 83802 table; 83803 83804 html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + (workingDate.getFullYear() + this.Locale.YEAR_OFFSET) +'" id="' + this.id + '">'; 83805 html = this.renderHeader(html); 83806 html = this.renderBody(workingDate, html); 83807 html = this.renderFooter(html); 83808 html[html.length] = '</table>'; 83809 83810 this.oDomContainer.innerHTML = html.join("\n"); 83811 83812 this.applyListeners(); 83813 83814 // Using oDomContainer.ownerDocument, to allow for cross-frame rendering 83815 table = ((this._oDoc) && this._oDoc.getElementById(this.id)) || (this.id
83815); 83816 83817 this.cells = Dom.getElementsByClassName(this.Style.CSS_CELL, "td", table); 83818 83819 this.cfg.refireEvent(DEF_CFG.TITLE.key); 83820 this.cfg.refireEvent(DEF_CFG.CLOSE.key); 83821 this.cfg.refireEvent(DEF_CFG.IFRAME.key); 83822 83823 this.renderEvent.fire(); 83824 }, 83825 83826 /** 83827 * Applies the Calendar's DOM listeners to applicable elements. 83828 * @method applyListeners 83829 */ 83830 applyListeners : function() { 83831 var root = this.oDomContainer, 83832 cal = this.parent || this, 83833 anchor = "a", 83834 click = "click"; 83835 83836 var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root), 83837 linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root); 83838 83839 if (linkLeft && linkLeft.length > 0) { 83840 this.linkLeft = linkLeft[0]; 83841 Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true); 83842 } 83843 83844 if (linkRight && linkRight.length > 0) { 83845 this.linkRight = linkRight[0]; 83846 Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true); 83847 } 83848 83849 if (cal.cfg.getProperty("navigator") !== null) { 83850 this.applyNavListeners(); 83851 } 83852 83853 if (this.domEventMap) { 83854 var el,elements; 83855 for (var cls in this.domEventMap) { 83856 if (Lang.hasOwnProperty(this.domEventMap, cls)) { 83857 var items = this.domEventMap[cls]; 83858 83859 if (! (items instanceof Array)) { 83860 items = [items]; 83861 } 83862 83863 for (var i=0;i<items.length;i++) { 83864 var item = items[i]; 83865 elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer); 83866 83867 for (var c=0;c<elements.length;c++) { 83868 el = elements[c]; 83869 Event.addListener(el, item.event, item.handler, item.scope, item.correct ); 83870 } 83871 } 83872 } 83873 } 83874 } 83875 83876 Event.addListener(this.oDomContainer, "click", this.doSelectCell, this); 83877 Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this); 83878 Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this); 83879 }, 83880 83881 /** 83882 * Applies the DOM listeners to activate the Calendar Navigator. 83883 * @method applyNavListeners 83884 */ 83885 applyNavListeners : function() { 83886 var calParent = this.parent || this, 83887 cal = this, 83888 navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer); 83889 83890 if (navBtns.length > 0) { 83891 83892 Event.addListener(navBtns, "click", function (e, obj) { 83893 var target = Event.getTarget(e); 83894 // this == navBtn 83895 if (this === target || Dom.isAncestor(this, target)) { 83896 Event.preventDefault(e); 83897 } 83898 var navigator = calParent.oNavigator; 83899 if (navigator) { 83900 var pgdate = cal.cfg.getProperty("pagedate"); 83901 navigator.setYear(pgdate.getFullYear() + cal.Locale.YEAR_OFFSET); 83902 navigator.setMonth(pgdate.getMonth()); 83903 navigator.show(); 83904 } 83905 }); 83906 } 83907 }, 83908 83909 /** 83910 * Retrieves the Date object for the specified Calendar cell 83911 * @method getDateByCellId 83912 * @param {String} id The id of the cell 83913 * @return {Date} The Date object for the specified Calendar cell 83914 */ 83915 getDateByCellId : function(id) { 83916 var date = this.getDateFieldsByCellId(id); 83917 return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null; 83918 }, 83919 83920 /** 83921 * Retrieves the Date object for the specified Calendar cell 83922 * @method getDateFieldsByCellId 83923 * @param {String} id The id of the cell 83924 * @return {Array} The array of Date fields for the specified Calendar cell 83925 */ 83926 getDateFieldsByCellId : function(id) { 83927 id = this.getIndexFromId(id); 83928 return (id > -1) ? this.cellDates[id] : null; 83929 }, 83930 83931 /** 83932 * Find the Calendar's cell index for a given date. 83933 * If the date is not found, the method returns -1. 83934 * <p> 83935 * The returned index can be used to lookup the cell HTMLElement 83936 * using the Calendar's cells array or passed to selectCell to select 83937 * cells by index. 83938 * </p> 83939 * 83940 * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>. 83941 * 83942 * @method getCellIndex 83943 * @param {Date} date JavaScript Date object, for which to find a cell index. 83944 * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date 83945 * is not on the curently rendered Calendar page. 83946 */ 83947 getCellIndex : function(date) { 83948 var idx = -1; 83949 if (date) { 83950 var m = date.getMonth(), 83951 y = date.getFullYear(), 83952 d = date.getDate(), 83953 dates = this.cellDates; 83954 83955 for (var i = 0; i < dates.length; ++i) { 83956 var cellDate = dates[i]; 83957 if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) { 83958 idx = i; 83959 break; 83960 } 83961 } 83962 } 83963 return idx; 83964 }, 83965 83966 /** 83967 * Given the id used to mark each Calendar cell, this method 83968 * extracts the index number from the id. 83969 * 83970 * @param {String} strId The cell id 83971 * @return {Number} The index of the cell, or -1 if id does not contain an index number 83972 */ 83973 getIndexFromId : function(strId) { 83974 var idx = -1, 83975 li = strId.lastIndexOf("_cell"); 83976 83977 if (li > -1) { 83978 idx = parseInt(strId.substring(li + 5), 10); 83979 } 83980 83981 return idx; 83982 }, 83983 83984 // BEGIN BUILT-IN TABLE CELL RENDERERS 83985 83986 /** 83987 * Renders a cell that falls before the minimum date or after the maximum date. 83988 * @method renderOutOfBoundsDate 83989 * @param {Date} workingDate The current working Date object being used to generate the calendar 83990 * @param {HTMLTableCellElement} cell The current working cell in the calendar 83991 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering 83992 * should not be terminated 83993 */ 83994 renderOutOfBoundsDate : function(workingDate, cell) { 83995 Dom.addClass(cell, this.Style.CSS_CELL_OOB); 83996 cell.innerHTML = workingDate.getDate(); 83997 return Calendar.STOP_RENDER; 83998 }, 83999 84000 /** 84001 * Renders the row header HTML for a week. 84002 * 84003 * @method renderRowHeader 84004 * @param {Number} weekNum The week number of the current row 84005 * @param {HTML[]} cell The current working HTML array 84006 */ 84007 renderRowHeader : function(weekNum, html) { 84008 html[html.length] = '<th class="' + this.Style.CSS_ROW_HEADER + '">' + weekNum + '</th>'; 84009 return html; 84010 }, 84011 84012 /** 84013 * Renders the row footer HTML for a week. 84014 * 84015 * @method renderRowFooter 84016 * @param {Number} weekNum The week number of the current row 84017 * @param {HTML[]} cell The current working HTML array 84018 */ 84019 renderRowFooter : function(weekNum, html) { 84020 html[html.length] = '<th class="' + this.Style.CSS_ROW_FOOTER + '">' + weekNum + '</th>'; 84021 return html; 84022 }, 84023 84024 /** 84025 * Renders a single standard calendar cell in the calendar widget table. 84026 * 84027 * All logic for determining how a standard default cell will be rendered is 84028 * encapsulated in this method, and must be accounted for when extending the 84029 * widget class. 84030 * 84031 * @method renderCellDefault 84032 * @param {Date} workingDate The current working Date object being used to generate the calendar 84033 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84034 */ 84035 renderCellDefault : function(workingDate, cell) { 84036 cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>"; 84037 }, 84038 84039 /** 84040 * Styles a selectable cell. 84041 * @method styleCellDefault 84042 * @param {Date} workingDate The current working Date object being used to generate the calendar 84043 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84044 */ 84045 styleCellDefault : function(workingDate, cell) { 84046 Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE); 84047 }, 84048 84049 84050 /** 84051 * Renders a single standard calendar cell using the CSS hightlight1 style 84052 * @method renderCellStyleHighlight1 84053 * @param {Date} workingDate The current working Date object being used to generate the calendar 84054 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84055 */ 84056 renderCellStyleHighlight1 : function(workingDate, cell) { 84057 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1); 84058 }, 84059 84060 /** 84061 * Renders a single standard calendar cell using the CSS hightlight2 style 84062 * @method renderCellStyleHighlight2 84063 * @param {Date} workingDate The current working Date object being used to generate the calendar 84064 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84065 */ 84066 renderCellStyleHighlight2 : function(workingDate, cell) { 84067 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2); 84068 }, 84069 84070 /** 84071 * Renders a single standard calendar cell using the CSS hightlight3 style 84072 * @method renderCellStyleHighlight3 84073 * @param {Date} workingDate The current working Date object being used to generate the calendar 84074 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84075 */ 84076 renderCellStyleHighlight3 : function(workingDate, cell) { 84077 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3); 84078 }, 84079 84080 /** 84081 * Renders a single standard calendar cell using the CSS hightlight4 style 84082 * @method renderCellStyleHighlight4 84083 * @param {Date} workingDate The current working Date object being used to generate the calendar 84084 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84085 */ 84086 renderCellStyleHighlight4 : function(workingDate, cell) { 84087 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4); 84088 }, 84089 84090 /** 84091 * Applies the default style used for rendering today's date to the current calendar cell 84092 * @method renderCellStyleToday 84093 * @param {Date} workingDate The current working Date object being used to generate the calendar 84094 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84095 */ 84096 renderCellStyleToday : function(workingDate, cell) { 84097 Dom.addClass(cell, this.Style.CSS_CELL_TODAY); 84098 }, 84099 84100 /** 84101 * Applies the default style used for rendering selected dates to the current calendar cell 84102 * @method renderCellStyleSelected 84103 * @param {Date} workingDate The current working Date object being used to generate the calendar 84104 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84105 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering 84106 * should not be terminated 84107 */ 84108 renderCellStyleSelected : function(workingDate, cell) { 84109 Dom.addClass(cell, this.Style.CSS_CELL_SELECTED); 84110 }, 84111 84112 /** 84113 * Applies the default style used for rendering dates that are not a part of the current 84114 * month (preceding or trailing the cells for the current month) 84115 * 84116 * @method renderCellNotThisMonth 84117 * @param {Date} workingDate The current working Date object being used to generate the calendar 84118 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84119 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering 84120 * should not be terminated 84121 */ 84122 renderCellNotThisMonth : function(workingDate, cell) { 84123 this.styleCellNotThisMonth(workingDate, cell); 84124 cell.innerHTML=workingDate.getDate(); 84125 return Calendar.STOP_RENDER; 84126 }, 84127 84128 /** Applies the style used for rendering out-of-month dates to the current calendar cell 84129 * @method styleCellNotThisMonth 84130 * @param {Date} workingDate The current working Date object being used to generate the calendar 84131 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84132 */ 84133 styleCellNotThisMonth : function(workingDate, cell) { 84134 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM); 84135 }, 84136 84137 /** 84138 * Renders the current calendar cell as a non-selectable "black-out" date using the default 84139 * restricted style. 84140 * @method renderBodyCellRestricted 84141 * @param {Date} workingDate The current working Date object being used to generate the calendar 84142 * @param {HTMLTableCellElement} cell The current working cell in the calendar 84143 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering 84144 * should not be terminated 84145 */ 84146 renderBodyCellRestricted : function(workingDate, cell) { 84147 Dom.addClass(cell, this.Style.CSS_CELL); 84148 Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED); 84149 cell.innerHTML=workingDate.getDate(); 84150 return Calendar.STOP_RENDER; 84151 }, 84152 84153 // END BUILT-IN TABLE CELL RENDERERS 84154 84155 // BEGIN MONTH NAVIGATION METHODS 84156 84157 /** 84158 * Adds the designated number of months to the current calendar month, and sets the current 84159 * calendar page date to the new month. 84160 * @method addMonths 84161 * @param {Number} count The number of months to add to the current calendar 84162 */ 84163 addMonths : function(count) { 84164 var cfgPageDate = DEF_CFG.PAGEDATE.key, 84165 84166 prevDate = this.cfg.getProperty(cfgPageDate), 84167 newDate = DateMath.add(prevDate, DateMath.MONTH, count); 84168
84169 this.cfg.setProperty(cfgPageDate, newDate); 84170 this.resetRenderers(); 84171 this.changePageEvent.fire(prevDate, newDate); 84172 }, 84173 84174 /** 84175 * Subtracts the designated number of months from the current calendar month, and sets the current 84176 * calendar page date to the new month. 84177 * @method subtractMonths 84178 * @param {Number} count The number of months to subtract from the current calendar 84179 */ 84180 subtractMonths : function(count) { 84181 this.addMonths(-1*count); 84182 }, 84183 84184 /** 84185 * Adds the designated number of years to the current calendar, and sets the current 84186 * calendar page date to the new month. 84187 * @method addYears 84188 * @param {Number} count The number of years to add to the current calendar 84189 */ 84190 addYears : function(count) { 84191 var cfgPageDate = DEF_CFG.PAGEDATE.key, 84192 84193 prevDate = this.cfg.getProperty(cfgPageDate), 84194 newDate = DateMath.add(prevDate, DateMath.YEAR, count); 84195 84196 this.cfg.setProperty(cfgPageDate, newDate); 84197 this.resetRenderers(); 84198 this.changePageEvent.fire(prevDate, newDate); 84199 }, 84200 84201 /** 84202 * Subtcats the designated number of years from the current calendar, and sets the current 84203 * calendar page date to the new month. 84204 * @method subtractYears 84205 * @param {Number} count The number of years to subtract from the current calendar 84206 */ 84207 subtractYears : function(count) { 84208 this.addYears(-1*count); 84209 }, 84210 84211 /** 84212 * Navigates to the next month page in the calendar widget. 84213 * @method nextMonth 84214 */ 84215 nextMonth : function() { 84216 this.addMonths(1); 84217 }, 84218 84219 /** 84220 * Navigates to the previous month page in the calendar widget. 84221 * @method previousMonth 84222 */ 84223 previousMonth : function() { 84224 this.addMonths(-1); 84225 }, 84226 84227 /** 84228 * Navigates to the next year in the currently selected month in the calendar widget. 84229 * @method nextYear 84230 */ 84231 nextYear : function() { 84232 this.addYears(1); 84233 }, 84234 84235 /** 84236 * Navigates to the previous year in the currently selected month in the calendar widget. 84237 * @method previousYear 84238 */ 84239 previousYear : function() { 84240 this.addYears(-1); 84241 }, 84242 84243 // END MONTH NAVIGATION METHODS 84244 84245 // BEGIN SELECTION METHODS 84246 84247 /** 84248 * Resets the calendar widget to the originally selected month and year, and 84249 * sets the calendar to the initial selection(s). 84250 * @method reset 84251 */ 84252 reset : function() { 84253 this.cfg.resetProperty(DEF_CFG.SELECTED.key); 84254 this.cfg.resetProperty(DEF_CFG.PAGEDATE.key); 84255 this.resetEvent.fire(); 84256 }, 84257 84258 /** 84259 * Clears the selected dates in the current calendar widget and sets the calendar 84260 * to the current month and year. 84261 * @method clear 84262 */ 84263 clear : function() { 84264 this.cfg.setProperty(DEF_CFG.SELECTED.key, []); 84265 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime())); 84266 this.clearEvent.fire(); 84267 }, 84268 84269 /** 84270 * Selects a date or a collection of dates on the current calendar. This method, by default, 84271 * does not call the render method explicitly. Once selection has completed, render must be 84272 * called for the changes to be reflected visually. 84273 * 84274 * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of 84275 * selected dates passed to the selectEvent will not contain OOB dates. 84276 * 84277 * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired. 84278 * 84279 * @method select 84280 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are 84281 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006). 84282 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005). 84283 * This method can also take a JavaScript Date object or an array of Date objects. 84284 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 84285 */ 84286 select : function(date) { 84287 this.logger.log("Select: " + date, "info"); 84288 84289 var aToBeSelected = this._toFieldArray(date), 84290 validDates = [], 84291 selected = [], 84292 cfgSelected = DEF_CFG.SELECTED.key; 84293 84294 this.logger.log("Selection field array: " + aToBeSelected, "info"); 84295 84296 for (var a=0; a < aToBeSelected.length; ++a) { 84297 var toSelect = aToBeSelected[a]; 84298 84299 if (!this.isDateOOB(this._toDate(toSelect))) { 84300 84301 if (validDates.length === 0) { 84302 this.beforeSelectEvent.fire(); 84303 selected = this.cfg.getProperty(cfgSelected); 84304 } 84305 validDates.push(toSelect); 84306 84307 if (this._indexOfSelectedFieldArray(toSelect) == -1) { 84308 selected[selected.length] = toSelect; 84309 } 84310 } 84311 } 84312 84313 if (validDates.length === 0) { this.logger.log("All provided dates were OOB. beforeSelect and select events not fired", "info"); } 84314 84315 if (validDates.length > 0) { 84316 if (this.parent) { 84317 this.parent.cfg.setProperty(cfgSelected, selected); 84318 } else { 84319 this.cfg.setProperty(cfgSelected, selected); 84320 } 84321 this.selectEvent.fire(validDates); 84322 } 84323
84324 return this.getSelectedDates(); 84325 }, 84326 84327 /** 84328 * Selects a date on the current calendar by referencing the index of the cell that should be selected. 84329 * This method is used to easily select a single cell (usually with a mouse click) without having to do 84330 * a full render. The selected style is applied to the cell directly. 84331 * 84332 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 84333 * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired. 84334 * 84335 * @method selectCell 84336 * @param {Number} cellIndex The index of the cell to select in the current calendar. 84337 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 84338 */ 84339 selectCell : function(cellIndex) { 84340 84341 var cell = this.cells[cellIndex], 84342 cellDate = this.cellDates[cellIndex], 84343 dCellDate = this._toDate(cellDate), 84344 selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE); 84345 84346 this.logger.log("Select: " + dCellDate, "info"); 84347 if (!selectable) {this.logger.log("The cell at cellIndex:" + cellIndex + " is not a selectable cell. beforeSelect, select events not fired", "info"); } 84348 84349 if (selectable) { 84350 84351 this.beforeSelectEvent.fire(); 84352 84353 var cfgSelected = DEF_CFG.SELECTED.key; 84354 var selected = this.cfg.getProperty(cfgSelected); 84355 84356 var selectDate = cellDate.concat(); 84357 84358 if (this._indexOfSelectedFieldArray(selectDate) == -1) { 84359 selected[selected.length] = selectDate; 84360 } 84361 if (this.parent) { 84362 this.parent.cfg.setProperty(cfgSelected, selected); 84363 } else { 84364 this.cfg.setProperty(cfgSelected, selected); 84365 } 84366 this.renderCellStyleSelected(dCellDate,cell); 84367 this.selectEvent.fire([selectDate]); 84368 84369 this.doCellMouseOut.call(cell, null, this); 84370 } 84371 84372 return this.getSelectedDates(); 84373 }, 84374 84375 /** 84376 * Deselects a date or a collection of dates on the current calendar. This method, by default, 84377 * does not call the render method explicitly. Once deselection has completed, render must be 84378 * called for the changes to be reflected visually. 84379 * 84380 * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable) 84381 * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates. 84382 * 84383 * If all dates are OOB, beforeDeselect and deselect events will not be fired. 84384 * 84385 * @method deselect 84386 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are 84387 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006). 84388 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005). 84389 * This method can also take a JavaScript Date object or an array of Date objects. 84390 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 84391 */ 84392 deselect : function(date) { 84393 this.logger.log("Deselect: " + date, "info"); 84394 84395 var aToBeDeselected = this._toFieldArray(date), 84396 validDates = [], 84397 selected = [], 84398 cfgSelected = DEF_CFG.SELECTED.key; 84399 84400 this.logger.log("Deselection field array: " + aToBeDeselected, "info"); 84401 84402 for (var a=0; a < aToBeDeselected.length; ++a) { 84403 var toDeselect = aToBeDeselected[a]; 84404 84405 if (!this.isDateOOB(this._toDate(toDeselect))) { 84406 84407 if (validDates.length === 0) { 84408 this.beforeDeselectEvent.fire(); 84409 selected = this.cfg.getProperty(cfgSelected); 84410 } 84411 84412 validDates.push(toDeselect); 84413 84414 var index = this._indexOfSelectedFieldArray(toDeselect); 84415 if (index != -1) { 84416 selected.splice(index,1); 84417 } 84418 } 84419 } 84420 84421 if (validDates.length === 0) { this.logger.log("All provided dates were OOB. beforeDeselect and deselect events not fired");} 84422 84423 if (validDates.length > 0) { 84424 if (this.parent) { 84425 this.parent.cfg.setProperty(cfgSelected, selected); 84426 } else { 84427 this.cfg.setProperty(cfgSelected, selected); 84428 } 84429 this.deselectEvent.fire(validDates); 84430 } 84431
84432 return this.getSelectedDates(); 84433 }, 84434 84435 /** 84436 * Deselects a date on the current calendar by referencing the index of the cell that should be deselected. 84437 * This method is used to easily deselect a single cell (usually with a mouse click) without having to do 84438 * a full render. The selected style is removed from the cell directly. 84439 * 84440 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 84441 * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and 84442 * deselect events will not be fired. 84443 * 84444 * @method deselectCell 84445 * @param {Number} cellIndex The index of the cell to deselect in the current calendar. 84446 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 84447 */ 84448 deselectCell : function(cellIndex) { 84449 var cell = this.cells[cellIndex], 84450 cellDate = this.cellDates[cellIndex], 84451 cellDateIndex = this._indexOfSelectedFieldArray(cellDate); 84452 84453 var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE); 84454 if (!selectable) { this.logger.log("The cell at cellIndex:" + cellIndex + " is not a selectable/deselectable cell", "info"); } 84455 84456 if (selectable) { 84457 84458 this.beforeDeselectEvent.fire(); 84459 84460 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key), 84461 dCellDate = this._toDate(cellDate), 84462 selectDate = cellDate.concat(); 84463 84464 if (cellDateIndex > -1) { 84465 if ((this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() && 84466 this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) || this.cfg.getProperty(DEF_CFG.OOM_SELECT.key)) { 84467 Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED); 84468 } 84469 selected.splice(cellDateIndex, 1); 84470 } 84471 84472 if (this.parent) { 84473 this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected); 84474 } else { 84475 this.cfg.setProperty(DEF_CFG.SELECTED.key, selected); 84476 } 84477 84478 this.deselectEvent.fire([selectDate]); 84479 } 84480 84481 return this.getSelectedDates(); 84482 }, 84483 84484 /** 84485 * Deselects all dates on the current calendar. 84486 * @method deselectAll 84487 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 84488 * Assuming that this function executes properly, the return value should be an empty array. 84489 * However, the empty array is returned for the sake of being able to check the selection status 84490 * of the calendar. 84491 */ 84492 deselectAll : function() { 84493 this.beforeDeselectEvent.fire(); 84494 84495 var cfgSelected = DEF_CFG.SELECTED.key, 84496 selected = this.cfg.getProperty(cfgSelected), 84497 count = selected.length, 84498 sel = selected.concat(); 84499 84500 if (this.parent) { 84501 this.parent.cfg.setProperty(cfgSelected, []); 84502 } else { 84503 this.cfg.setProperty(cfgSelected, []); 84504 } 84505 84506 if (count > 0) { 84507 this.deselectEvent.fire(sel); 84508 } 84509 84510 return this.getSelectedDates(); 84511 }, 84512 84513 // END SELECTION METHODS 84514 84515 // BEGIN TYPE CONVERSION METHODS 84516 84517 /** 84518 * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure 84519 * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]]. 84520 * @method _toFieldArray 84521 * @private 84522 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are 84523 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006). 84524 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005). 84525 * This method can also take a JavaScript Date object or an array of Date objects. 84526 * @return {Array[](Number[])} Array of date field arrays 84527 */ 84528 _toFieldArray : function(date) { 84529 var returnDate = []; 84530 84531 if (date instanceof Date) { 84532 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]]; 84533 } else if (Lang.isString(date)) { 84534 returnDate = this._parseDates(date); 84535 } else if (Lang.isArray(date)) { 84536 for (var i=0;i<date.length;++i) { 84537 var d = date[i]; 84538 returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()]; 84539 } 84540 } 84541 84542 return returnDate; 84543 }, 84544 84545 /** 84546 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array 84547 * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners. 84548 * 84549 * @method toDate 84550 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date. 84551 * @return {Date} JavaScript Date object representing the date field array.
84552 */ 84553 toDate : function(dateFieldArray) { 84554 return this._toDate(dateFieldArray); 84555 }, 84556 84557 /** 84558 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. 84559 * @method _toDate 84560 * @private 84561 * @deprecated Made public, toDate 84562 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date. 84563 * @return {Date} JavaScript Date object representing the date field array 84564 */ 84565 _toDate : function(dateFieldArray) { 84566 if (dateFieldArray instanceof Date) { 84567 return dateFieldArray; 84568 } else { 84569 return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]); 84570 } 84571 }, 84572 84573 // END TYPE CONVERSION METHODS 84574 84575 // BEGIN UTILITY METHODS 84576 84577 /** 84578 * Determines if 2 field arrays are equal. 84579 * @method _fieldArraysAreEqual 84580 * @private 84581 * @param {Number[]} array1 The first date field array to compare 84582 * @param {Number[]} array2 The first date field array to compare 84583 * @return {Boolean} The boolean that represents the equality of the two arrays 84584 */ 84585 _fieldArraysAreEqual : function(array1, array2) { 84586 var match = false; 84587 84588 if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) { 84589 match=true; 84590 } 84591 84592 return match; 84593 }, 84594 84595 /** 84596 * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates. 84597 * @method _indexOfSelectedFieldArray 84598 * @private 84599 * @param {Number[]} find The date field array to search for 84600 * @return {Number} The index of the date field array within the collection of selected dates. 84601 * -1 will be returned if the date is not found. 84602 */ 84603 _indexOfSelectedFieldArray : function(find) { 84604 var selected = -1, 84605 seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key); 84606 84607 for (var s=0;s<seldates.length;++s) { 84608 var sArray = seldates[s]; 84609 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) { 84610 selected = s; 84611 break; 84612 } 84613 } 84614 84615 return selected; 84616 }, 84617 84618 /** 84619 * Determines whether a given date is OOM (out of month). 84620 * @method isDateOOM 84621 * @param {Date} date The JavaScript Date object for which to check the OOM status 84622 * @return {Boolean} true if the date is OOM 84623 */ 84624 isDateOOM : function(date) { 84625 return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth()); 84626 }, 84627 84628 /** 84629 * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate). 84630 * 84631 * @method isDateOOB 84632 * @param {Date} date The JavaScript Date object for which to check the OOB status 84633 * @return {Boolean} true if the date is OOB 84634 */ 84635 isDateOOB : function(date) { 84636 var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key), 84637 maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key), 84638 dm = DateMath; 84639 84640 if (minDate) { 84641 minDate = dm.clearTime(minDate); 84642 } 84643 if (maxDate) { 84644 maxDate = dm.clearTime(maxDate); 84645 } 84646 84647 var clearedDate = new Date(date.getTime()); 84648 clearedDate = dm.clearTime(clearedDate); 84649 84650 return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime())); 84651 }, 84652 84653 /** 84654 * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object 84655 * and is parsed into a Date object normalized to the first day of the month. If no value is passed in, the month and year from today's date are used to create the Date object 84656 * @method _parsePageDate 84657 * @private 84658 * @param {Date|String} date Pagedate value which needs to be parsed 84659 * @return {Date} The Date object representing the pagedate 84660 */ 84661 _parsePageDate : function(date) { 84662 var parsedDate; 84663 84664 if (date) { 84665 if (date instanceof Date) { 84666 parsedDate = DateMath.findMonthStart(date); 84667 } else { 84668 var month, year, aMonthYear; 84669 aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
84670 month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1; 84671 year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10) - this.Locale.YEAR_OFFSET; 84672 84673 parsedDate = DateMath.getDate(year, month, 1); 84674 } 84675 } else { 84676 parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1); 84677 } 84678 return parsedDate; 84679 }, 84680 84681 // END UTILITY METHODS 84682 84683 // BEGIN EVENT HANDLERS 84684 84685 /** 84686 * Event executed before a date is selected in the calendar widget. 84687 * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent. 84688 */ 84689 onBeforeSelect : function() { 84690 if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) { 84691 if (this.parent) { 84692 this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED); 84693 this.parent.deselectAll(); 84694 } else { 84695 this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED); 84696 this.deselectAll(); 84697 } 84698 } 84699 }, 84700 84701 /** 84702 * Event executed when a date is selected in the calendar widget. 84703 * @param {Array} selected An array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ] 84704 * @deprecated Event handlers for this event should be susbcribed to selectEvent. 84705 */ 84706 onSelect : function(selected) { }, 84707 84708 /** 84709 * Event executed before a date is deselected in the calendar widget. 84710 * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent. 84711 */ 84712 onBeforeDeselect : function() { }, 84713 84714 /** 84715 * Event executed when a date is deselected in the calendar widget. 84716 * @param {Array} selected An array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ] 84717 * @deprecated Event handlers for this event should be susbcribed to deselectEvent. 84718 */ 84719 onDeselect : function(deselected) { }, 84720 84721 /** 84722 * Event executed when the user navigates to a different calendar page. 84723 * @deprecated Event handlers for this event should be susbcribed to changePageEvent. 84724 */ 84725 onChangePage : function() { 84726 this.render(); 84727 }, 84728 84729 /** 84730 * Event executed when the calendar widget is rendered. 84731 * @deprecated Event handlers for this event should be susbcribed to renderEvent. 84732 */ 84733 onRender : function() { }, 84734 84735 /** 84736 * Event executed when the calendar widget is reset to its original state. 84737 * @deprecated Event handlers for this event should be susbcribed to resetEvemt. 84738 */ 84739 onReset : function() { this.render(); }, 84740 84741 /** 84742 * Event executed when the calendar widget is completely cleared to the current month with no selections. 84743 * @deprecated Event handlers for this event should be susbcribed to clearEvent. 84744 */ 84745 onClear : function() { this.render(); }, 84746 84747 /** 84748 * Validates the calendar widget. This method has no default implementation 84749 * and must be extended by subclassing the widget. 84750 * @return Should return true if the widget validates, and false if 84751 * it doesn't. 84752 * @type Boolean 84753 */ 84754 validate : function() { return true; }, 84755 84756 // END EVENT HANDLERS 84757 84758 // BEGIN DATE PARSE METHODS 84759 84760 /** 84761 * Converts a date string to a date field array 84762 * @private 84763 * @param {String} sDate Date string. Valid formats are mm/dd and mm/dd/yyyy. 84764 * @return A date field array representing the string passed to the method 84765 * @type Array[](Number[]) 84766 */ 84767 _parseDate : function(sDate) { 84768 var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER), 84769 rArray; 84770 84771 if (aDate.length == 2) { 84772 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]]; 84773 rArray.type = Calendar.MONTH_DAY; 84774 } else { 84775 rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1] - this.Locale.YEAR_OFFSET, aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]]; 84776 rArray.type = Calendar.DATE; 84777 } 84778 84779 for (var i=0;i<rArray.length;i++) { 84780 rArray[i] = parseInt(rArray[i], 10); 84781 } 84782 84783 return rArray; 84784 }, 84785 84786 /** 84787 * Converts a multi or single-date string to an array of date field arrays 84788 * @private 84789 * @param {String} sDates Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy 84790 * @return An array of date field arrays 84791 * @type Array[](Number[]) 84792 */ 84793 _parseDates : function(sDates) { 84794 var aReturn = [], 84795 aDates = sDates.split(this.Locale.DATE_DELIMITER); 84796 84797 for (var d=0;d<aDates.length;++d) { 84798 var sDate = aDates[d]; 84799 84800 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) { 84801 // This is a range 84802 var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER), 84803 dateStart = this._parseDate(aRange[0]), 84804 dateEnd = this._parseDate(aRange[1]), 84805 fullRange = this._parseRange(dateStart, dateEnd); 84806 84807 aReturn = aReturn.concat(fullRange); 84808 } else { 84809 // This is not a range 84810 var aDate = this._parseDate(sDate); 84811 aReturn.push(aDate); 84812 } 84813 } 84814 return aReturn; 84815 }, 84816 84817 /** 84818 * Converts a date range to the full list of included dates 84819 * @private 84820 * @param {Number[]} startDate Date field array representing the first date in the range 84821 * @param {Number[]} endDate Date field array representing the last date in the range 84822 * @return An array of date field arrays 84823 * @type Array[](Number[]) 84824 */ 84825 _parseRange : function(startDate, endDate) { 84826 var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1), 84827 dEnd = DateMath.getDate(endDate[0], endDate[1]-1, endDate[2]), 84828 results = []; 84829 84830 results.push(startDate); 84831 while (dCurrent.getTime() <= dEnd.getTime()) { 84832 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]); 84833 dCurrent = DateMath.add(dCurrent,DateMath.DAY,1); 84834 } 84835 return results; 84836 }, 84837 84838 // END DATE PARSE METHODS 84839 84840 // BEGIN RENDERER METHODS 84841 84842 /** 84843 * Resets the render stack of the current calendar to its original pre-render value. 84844 */ 84845 resetRenderers : function() { 84846 this.renderStack = this._renderStack.concat(); 84847 }, 84848 84849 /** 84850 * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and 84851 * addWeekdayRenderer methods. Calendar's render method needs to be called after remov
84851ing renderers 84852 * to re-render the Calendar without custom renderers applied. 84853 */ 84854 removeRenderers : function() { 84855 this._renderStack = []; 84856 this.renderStack = []; 84857 }, 84858 84859 /** 84860 * Clears the inner HTML, CSS class and style information from the specified cell. 84861 * @method clearElement 84862 * @param {HTMLTableCellElement} cell The cell to clear 84863 */ 84864 clearElement : function(cell) { 84865 cell.innerHTML = " "; 84866 cell.className=""; 84867 }, 84868 84869 /** 84870 * Adds a renderer to the render stack. The function reference passed to this method will be executed 84871 * when a date cell matches the conditions specified in the date string for this renderer. 84872 * 84873 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 84874 * escape markup used to set the cell contents, if coming from an external source.<p> 84875 * @method addRenderer 84876 * @param {String} sDates A date string to associate with the specified renderer. Valid formats 84877 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005) 84878 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 84879 */ 84880 addRenderer : function(sDates, fnRender) { 84881 var aDates = this._parseDates(sDates); 84882 for (var i=0;i<aDates.length;++i) { 84883 var aDate = aDates[i]; 84884 84885 if (aDate.length == 2) { // this is either a range or a month/day combo 84886 if (aDate[0] instanceof Array) { // this is a range 84887 this._addRenderer(Calendar.RANGE,aDate,fnRender); 84888 } else { // this is a month/day combo 84889 this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender); 84890 } 84891 } else if (aDate.length == 3) { 84892 this._addRenderer(Calendar.DATE,aDate,fnRender); 84893 } 84894 } 84895 }, 84896 84897 /** 84898 * The private method used for adding cell renderers to the local render stack. 84899 * This method is called by other methods that set the renderer type prior to the method call. 84900 * @method _addRenderer 84901 * @private 84902 * @param {String} type The type string that indicates the type of date renderer being added. 84903 * Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY, 84904 * YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH 84905 * @param {Array} aDates An array of dates used to construct the renderer. The format varies based 84906 * on the renderer type 84907 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 84908 */ 84909 _addRenderer : function(type, aDates, fnRender) { 84910 var add = [type,aDates,fnRender]; 84911 this.renderStack.unshift(add); 84912 this._renderStack = this.renderStack.concat(); 84913 }, 84914 84915 /** 84916 * Adds a month renderer to the render stack. The function reference passed to this method will be executed 84917 * when a date cell matches the month passed to this method 84918 * 84919 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 84920 * escape markup used to set the cell contents, if coming from an external source.<p> 84921 * @method addMonthRenderer 84922 * @param {Number} month The month (1-12) to associate with this renderer 84923 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 84924 */ 84925 addMonthRenderer : function(month, fnRender) { 84926 this._addRenderer(Calendar.MONTH,[month],fnRender); 84927 }, 84928 84929 /** 84930 * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed 84931 * when a date cell matches the weekday passed to this method. 84932 * 84933 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 84934 * escape HTML used to set the cell contents, if coming from an external source.<p> 84935 * 84936 * @method addWeekdayRenderer 84937 * @param {Number} weekday The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer 84938 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 84939 */
84940 addWeekdayRenderer : function(weekday, fnRender) { 84941 this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender); 84942 }, 84943 84944 // END RENDERER METHODS 84945 84946 // BEGIN CSS METHODS 84947 84948 /** 84949 * Removes all styles from all body cells in the current calendar table. 84950 * @method clearAllBodyCellStyles 84951 * @param {style} style The CSS class name to remove from all calendar body cells 84952 */ 84953 clearAllBodyCellStyles : function(style) { 84954 for (var c=0;c<this.cells.length;++c) { 84955 Dom.removeClass(this.cells[c],style); 84956 } 84957 }, 84958 84959 // END CSS METHODS 84960 84961 // BEGIN GETTER/SETTER METHODS 84962 /** 84963 * Sets the calendar's month explicitly 84964 * @method setMonth 84965 * @param {Number} month The numeric month, from 0 (January) to 11 (December) 84966 */ 84967 setMonth : function(month) { 84968 var cfgPageDate = DEF_CFG.PAGEDATE.key, 84969 current = this.cfg.getProperty(cfgPageDate); 84970 current.setMonth(parseInt(month, 10)); 84971 this.cfg.setProperty(cfgPageDate, current); 84972 }, 84973 84974 /** 84975 * Sets the calendar's year explicitly. 84976 * @method setYear 84977 * @param {Number} year The numeric 4-digit year 84978 */ 84979 setYear : function(year) { 84980 var cfgPageDate = DEF_CFG.PAGEDATE.key, 84981 current = this.cfg.getProperty(cfgPageDate); 84982 84983 current.setFullYear(parseInt(year, 10) - this.Locale.YEAR_OFFSET); 84984 this.cfg.setProperty(cfgPageDate, current); 84985 }, 84986 84987 /** 84988 * Gets the list of currently selected dates from the calendar. 84989 * @method getSelectedDates 84990 * @return {Date[]} An array of currently selected JavaScript Date objects. 84991 */ 84992 getSelectedDates : function() { 84993 var returnDates = [], 84994 selected = this.cfg.getProperty(DEF_CFG.SELECTED.key); 84995 84996 for (var d=0;d<selected.length;++d) { 84997 var dateArray = selected[d]; 84998 84999 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]); 85000 returnDates.push(date); 85001 } 85002 85003 returnDates.sort( function(a,b) { return a-b; } ); 85004 return returnDates; 85005 }, 85006 85007 /// END GETTER/SETTER METHODS /// 85008 85009 /** 85010 * Hides the Calendar's outer container from view. 85011 * @method hide 85012 */ 85013 hide : function() { 85014 if (this.beforeHideEvent.fire()) { 85015 this.oDomContainer.style.display = "none"; 85016 this.hideEvent.fire(); 85017 } 85018 }, 85019 85020 /** 85021 * Shows the Calendar's outer container. 85022 * @method show 85023 */ 85024 show : function() { 85025 if (this.beforeShowEvent.fire()) { 85026 this.oDomContainer.style.display = "block"; 85027 this.showEvent.fire(); 85028 } 85029 }, 85030 85031 /** 85032 * Returns a string representing the current browser. 85033 * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua 85034 * @see YAHOO.env.ua 85035 * @property browser
85036 * @type String 85037 */ 85038 browser : (function() { 85039 var ua = navigator.userAgent.toLowerCase(); 85040 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof) 85041 return 'opera'; 85042 } else if (ua.indexOf('msie 7')!=-1) { // IE7 85043 return 'ie7'; 85044 } else if (ua.indexOf('msie') !=-1) { // IE 85045 return 'ie'; 85046 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko") 85047 return 'safari'; 85048 } else if (ua.indexOf('gecko') != -1) { // Gecko 85049 return 'gecko'; 85050 } else { 85051 return false; 85052 } 85053 })(), 85054 /** 85055 * Returns a string representation of the object. 85056 * @method toString 85057 * @return {String} A string representation of the Calendar object. 85058 */ 85059 toString : function() { 85060 return "Calendar " + this.id; 85061 }, 85062 85063 /** 85064 * Destroys the Calendar instance. The method will remove references 85065 * to HTML elements, remove any event listeners added by the Calendar, 85066 * and destroy the Config and CalendarNavigator instances it has created. 85067 * 85068 * @method destroy 85069 */ 85070 destroy : function() { 85071 85072 if (this.beforeDestroyEvent.fire()) { 85073 var cal = this; 85074 85075 // Child objects 85076 if (cal.navigator) { 85077 cal.navigator.destroy(); 85078 } 85079 85080 if (cal.cfg) { 85081 cal.cfg.destroy(); 85082 } 85083 85084 // DOM event listeners 85085 Event.purgeElement(cal.oDomContainer, true); 85086 85087 // Generated markup/DOM - Not removing the container DIV since we didn't create it. 85088 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_WITH_TITLE); 85089 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER); 85090 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE); 85091 cal.oDomContainer.innerHTML = ""; 85092 85093 // JS-to-DOM references 85094 cal.oDomContainer = null; 85095 cal.cells = null; 85096 85097 this.destroyEvent.fire(); 85098 } 85099 } 85100}; 85101 85102YAHOO.widget.Calendar = Calendar; 85103 85104/** 85105* @namespace YAHOO.widget 85106* @class Calendar_Core 85107* @extends YAHOO.widget.Calendar 85108* @deprecated The old Calendar_Core class is no longer necessary. 85109*/ 85110YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar; 85111 85112YAHOO.widget.Cal_Core = YAHOO.widget.Calendar; 85113 85114})(); 85115(function() { 85116 85117 var Dom = YAHOO.util.Dom, 85118 DateMath = YAHOO.widget.DateMath, 85119 Event = YAHOO.util.Event, 85120 Lang = YAHOO.lang, 85121 Calendar = YAHOO.widget.Calendar; 85122 85123/** 85124* YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates 85125* the ability to have multi-page calendar views that share a single dataset and are 85126* dependent on each other. 85127* 85128* The calendar group instance will refer to each of its elements using a 0-based index. 85129* For example, to construct the placeholder for a calendar group widget with id "cal1" and 85130* containerId of "cal1Container", the markup would be as follows: 85131* <xmp> 85132* <div id="cal1Container_0"></div> 85133* <div id="cal1Container_1"></div> 85134* </xmp> 85135* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers. 85136* 85137* <p> 85138* <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong> 85139* The CalendarGroup can be constructed by simply providing a container ID string, 85140* or a reference to a container DIV HTMLElement (the element needs to exist 85141* in the document). 85142* 85143* E.g.: 85144* <xmp> 85145* var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions); 85146* </xmp> 85147* or: 85148* <xmp> 85149* var containerDiv = YAHOO.util.Dom.get("calContainer"); 85150* var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions); 85151* </xmp> 85152* </p> 85153* <p> 85154* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix. 85155* For example if an ID is not provided, and the container's ID is "calContainer", the CalendarGroup's ID will be set to "calContainer_t". 85156* </p> 85157* 85158* @namespace YAHOO.widget 85159* @class CalendarGroup 85160* @constructor 85161* @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional. 85162* @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document. 85163* @param {Object}
85163 config optional The configuration object containing the initial configuration values for the CalendarGroup. 85164*/ 85165function CalendarGroup(id, containerId, config) { 85166 if (arguments.length > 0) { 85167 this.init.apply(this, arguments); 85168 } 85169} 85170 85171/** 85172* The set of default Config property keys and values for the CalendarGroup. 85173* 85174* <p> 85175* NOTE: This property is made public in order to allow users to change 85176* the default values of configuration properties. Users should not 85177* modify the key string, unless they are overriding the Calendar implementation 85178* </p> 85179* 85180* @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG 85181* @static 85182* @type Object An object with key/value pairs, the key being the 85183* uppercase configuration property name and the value being an objec 85184* literal with a key string property, and a value property, specifying the 85185* default value of the property 85186*/ 85187 85188/** 85189* The set of default Config property keys and values for the CalendarGroup 85190* @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG 85191* @deprecated Made public. See the public DEFAULT_CONFIG property for details 85192* @private 85193* @static 85194* @type Object 85195*/ 85196CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG; 85197CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2}; 85198 85199var DEF_CFG = CalendarGroup.DEFAULT_CONFIG; 85200 85201CalendarGroup.prototype = { 85202 85203 /** 85204 * Initializes the calendar group. All subclasses must call this method in order for the 85205 * group to be initialized properly. 85206 * @method init 85207 * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional. 85208 * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document. 85209 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup. 85210 */ 85211 init : function(id, container, config) { 85212 85213 // Normalize 2.4.0, pre 2.4.0 args 85214 var nArgs = this._parseArgs(arguments); 85215 85216 id = nArgs.id; 85217 container = nArgs.container; 85218 config = nArgs.config; 85219 85220 this.oDomContainer = Dom.get(container); 85221 if (!this.oDomContainer) { this.logger.log("Container not found in document.", "error"); } 85222 85223 if (!this.oDomContainer.id) { 85224 this.oDomContainer.id = Dom.generateId(); 85225 } 85226 if (!id) { 85227 id = this.oDomContainer.id + "_t"; 85228 } 85229 85230 /** 85231 * The unique id associated with the CalendarGroup 85232 * @property id
85233 * @type String 85234 */ 85235 this.id = id; 85236 85237 /** 85238 * The unique id associated with the CalendarGroup container 85239 * @property containerId 85240 * @type String 85241 */ 85242 this.containerId = this.oDomContainer.id; 85243 85244 this.logger = new YAHOO.widget.LogWriter("CalendarGroup " + this.id); 85245 this.initEvents(); 85246 this.initStyles(); 85247 85248 /** 85249 * The collection of Calendar pages contained within the CalendarGroup 85250 * @property pages 85251 * @type YAHOO.widget.Calendar[] 85252 */ 85253 this.pages = []; 85254 85255 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER); 85256 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP); 85257 85258 /** 85259 * The Config object used to hold the configuration variables for the CalendarGroup 85260 * @property cfg 85261 * @type YAHOO.util.Config 85262 */ 85263 this.cfg = new YAHOO.util.Config(this); 85264 85265 /** 85266 * The local object which contains the CalendarGroup's options 85267 * @property Options 85268 * @type Object 85269 */ 85270 this.Options = {}; 85271 85272 /** 85273 * The local object which contains the CalendarGroup's locale settings 85274 * @property Locale 85275 * @type Object 85276 */ 85277 this.Locale = {}; 85278 85279 this.setupConfig(); 85280 85281 if (config) { 85282 this.cfg.applyConfig(config, true); 85283 } 85284 85285 this.cfg.fireQueue(); 85286 85287 this.logger.log("Initialized " + this.pages.length + "-page CalendarGroup", "info"); 85288 }, 85289 85290 setupConfig : function() { 85291 85292 var cfg = this.cfg; 85293 85294 /** 85295 * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments. 85296 * @config pages 85297 * @type Number 85298 * @default 2 85299 */ 85300 cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } ); 85301 85302 /** 85303 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to 85304 * be used when displaying or parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by 85305 * methods will always represent the Gregorian year, in order to maintain date/month/week values. 85306 * 85307 * @config year_offset 85308 * @type Number 85309 * @default 0 85310 */ 85311 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } ); 85312 85313 /** 85314 * The date to use to represent "Today". 85315 * 85316 * @config today 85317 * @type Date 85318 * @default Today's date 85319 */ 85320 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } ); 85321 85322 /** 85323 * The month/year representing the current visible Calendar date (mm/yyyy) 85324 * @config pagedate
85325 * @type String | Date 85326 * @default Today's date 85327 */ 85328 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } ); 85329 85330 /** 85331 * The date or range of dates representing the current Calendar selection 85332 * 85333 * @config selected 85334 * @type String 85335 * @default [] 85336 */ 85337 cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } ); 85338 85339 /** 85340 * The title to display above the CalendarGroup's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85341 * @config title 85342 * @type HTML 85343 * @default "" 85344 */ 85345 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } ); 85346 85347 /** 85348 * Whether or not a close button should be displayed for this CalendarGroup 85349 * @config close 85350 * @type Boolean 85351 * @default false 85352 */ 85353 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } ); 85354 85355 /** 85356 * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below. 85357 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 85358 * enabled if required. 85359 * 85360 * @config iframe 85361 * @type Boolean 85362 * @default true for IE6 and below, false for all other browsers 85363 */ 85364 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } ); 85365 85366 /** 85367 * The minimum selectable date in the current Calendar (mm/dd/yyyy) 85368 * @config mindate
85369 * @type String | Date 85370 * @default null 85371 */ 85372 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } ); 85373 85374 /** 85375 * The maximum selectable date in the current Calendar (mm/dd/yyyy) 85376 * @config maxdate 85377 * @type String | Date 85378 * @default null 85379 */ 85380 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig } ); 85381 85382 /** 85383 * True if the Calendar should allow multiple selections. False by default. 85384 * @config MULTI_SELECT 85385 * @type Boolean 85386 * @default false 85387 */ 85388 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85389 85390 /** 85391 * True if the Calendar should allow selection of out-of-month dates. False by default. 85392 * @config OOM_SELECT 85393 * @type Boolean 85394 * @default false 85395 */ 85396 cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85397 85398 /** 85399 * The weekday the week begins on. Default is 0 (Sunday). 85400 * @config START_WEEKDAY 85401 * @type number 85402 * @default 0 85403 */ 85404 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85405 85406 /** 85407 * True if the Calendar should show weekday labels. True by default. 85408 * @config SHOW_WEEKDAYS 85409 * @type Boolean 85410 * @default true 85411 */ 85412 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85413 85414 /** 85415 * True if the Calendar should show week row headers. False by default. 85416 * @config SHOW_WEEK_HEADER 85417 * @type Boolean 85418 * @default false 85419 */ 85420 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85421 85422 /** 85423 * True if the Calendar should show week row footers. False by default. 85424 * @config SHOW_WEEK_FOOTER 85425 * @type Boolean 85426 * @default false 85427 */ 85428 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85429 85430 /** 85431 * True if the Calendar should suppress weeks that are not a part of the current month. False by default. 85432 * @config HIDE_BLANK_WEEKS 85433 * @type Boolean 85434 * @default false 85435 */ 85436 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); 85437 85438 /** 85439 * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85440 * @config NAV_ARROW_LEFT
85441 * @type String 85442 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft" 85443 * @default null 85444 */ 85445 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } ); 85446 85447 /** 85448 * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85449 * @config NAV_ARROW_RIGHT 85450 * @type String 85451 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright" 85452 * @default null 85453 */ 85454 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } ); 85455 85456 // Locale properties 85457 85458 /** 85459 * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85460 * @config MONTHS_SHORT 85461 * @type HTML[] 85462 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 85463 */ 85464 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } ); 85465 85466 /** 85467 * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85468 * @config MONTHS_LONG 85469 * @type HTML[] 85470 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
85470November", "December" 85471 */ 85472 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } ); 85473 85474 /** 85475 * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85476 * @config WEEKDAYS_1CHAR 85477 * @type HTML[] 85478 * @default ["S", "M", "T", "W", "T", "F", "S"] 85479 */ 85480 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } ); 85481 85482 /** 85483 * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85484 * @config WEEKDAYS_SHORT 85485 * @type HTML[] 85486 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] 85487 */ 85488 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } ); 85489 85490 /** 85491 * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85492 * @config WEEKDAYS_MEDIUM 85493 * @type HTML[] 85494 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] 85495 */ 85496 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } ); 85497 85498 /** 85499 * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85500 * @config WEEKDAYS_LONG 85501 * @type HTML[] 85502 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] 85503 */ 85504 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } ); 85505 85506 /** 85507 * The setting that determines which length of month labels should be used. Possible values are "short" and "long". 85508 * @config LOCALE_MONTHS
85509 * @type String 85510 * @default "long" 85511 */ 85512 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } ); 85513 85514 /** 85515 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long". 85516 * @config LOCALE_WEEKDAYS 85517 * @type String 85518 * @default "short" 85519 */ 85520 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } ); 85521 85522 /** 85523 * The value used to delimit individual dates in a date string passed to various Calendar functions. 85524 * @config DATE_DELIMITER 85525 * @type String 85526 * @default "," 85527 */ 85528 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } ); 85529 85530 /** 85531 * The value used to delimit date fields in a date string passed to various Calendar functions. 85532 * @config DATE_FIELD_DELIMITER
85533 * @type String 85534 * @default "/" 85535 */ 85536 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } ); 85537 85538 /** 85539 * The value used to delimit date ranges in a date string passed to various Calendar functions. 85540 * @config DATE_RANGE_DELIMITER 85541 * @type String 85542 * @default "-" 85543 */ 85544 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } ); 85545 85546 /** 85547 * The position of the month in a month/year date string 85548 * @config MY_MONTH_POSITION 85549 * @type Number 85550 * @default 1 85551 */ 85552 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85553 85554 /** 85555 * The position of the year in a month/year date string 85556 * @config MY_YEAR_POSITION 85557 * @type Number 85558 * @default 2 85559 */ 85560 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85561 85562 /** 85563 * The position of the month in a month/day date string 85564 * @config MD_MONTH_POSITION 85565 * @type Number 85566 * @default 1 85567 */ 85568 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85569 85570 /** 85571 * The position of the day in a month/year date string 85572 * @config MD_DAY_POSITION 85573 * @type Number 85574 * @default 2 85575 */ 85576 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85577 85578 /** 85579 * The position of the month in a month/day/year date string 85580 * @config MDY_MONTH_POSITION 85581 * @type Number 85582 * @default 1 85583 */ 85584 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85585 85586 /** 85587 * The position of the day in a month/day/year date string 85588 * @config MDY_DAY_POSITION 85589 * @type Number 85590 * @default 2 85591 */ 85592 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85593 85594 /** 85595 * The position of the year in a month/day/year date string 85596 * @config MDY_YEAR_POSITION 85597 * @type Number 85598 * @default 3 85599 */ 85600 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85601 85602 /** 85603 * The position of the month in the month year label string used as the Calendar header 85604 * @config MY_LABEL_MONTH_POSITION 85605 * @type Number 85606 * @default 1 85607 */ 85608 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85609 85610 /** 85611 * The position of the year in the month year label string used as the Calendar header 85612 * @config MY_LABEL_YEAR_POSITION 85613 * @type Number 85614 * @default 2 85615 */ 85616 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); 85617 85618 /** 85619 * The suffix used after the month when rendering the Calendar header 85620 * @config MY_LABEL_MONTH_SUFFIX
85621 * @type String 85622 * @default " " 85623 */ 85624 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } ); 85625 85626 /** 85627 * The suffix used after the year when rendering the Calendar header 85628 * @config MY_LABEL_YEAR_SUFFIX 85629 * @type String 85630 * @default "" 85631 */ 85632 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } ); 85633 85634 /** 85635 * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a 85636 * specific Month/Year without having to scroll sequentially through months. 85637 * <p> 85638 * Setting this property to null (default value) or false, will disable the CalendarNavigator UI. 85639 * </p> 85640 * <p> 85641 * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values. 85642 * </p> 85643 * <p> 85644 * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI. 85645 * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object. 85646 * Any properties which are not provided will use the default values (defined in the CalendarNavigator class). 85647 * </p> 85648 * <dl> 85649 * <dt>strings</dt> 85650 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 85651 * <dl> 85652 * <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd> 85653 * <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd> 85654 * <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd> 85655 * <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd> 85656 * <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd> 85657 * </dl> 85658 * </dd> 85659 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd> 85660 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd> 85661 * </dl> 85662 * <p>E.g.</p> 85663 * <pre> 85664 * var navConfig = { 85665 * strings: { 85666 * month:"Calendar Month", 85667 * year:"Calendar Year", 85668 * submit: "Submit", 85669 * cancel: "Cancel", 85670 * invalidYear: "Please enter a valid year" 85671 * }, 85672 * monthFormat: YAHOO.widget.Calendar.SHORT, 85673 * initialFocus: "month" 85674 * } 85675 * </pre> 85676 * @config navigator 85677 * @type {Object|Boolean} 85678 * @default null 85679 */ 85680 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } ); 85681 85682 /** 85683 * The map of UI strings which the CalendarGroup UI uses. 85684 * 85685 * @config strings 85686 * @type {Object} 85687 * @default An object with the properties shown below: 85688 * <dl> 85689 * <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd> 85690 * <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd> 85691 * <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
85692 * </dl> 85693 */ 85694 cfg.addProperty(DEF_CFG.STRINGS.key, { 85695 value:DEF_CFG.STRINGS.value, 85696 handler:this.configStrings, 85697 validator: function(val) { 85698 return Lang.isObject(val); 85699 }, 85700 supercedes: DEF_CFG.STRINGS.supercedes 85701 }); 85702 }, 85703 85704 /** 85705 * Initializes CalendarGroup's built-in CustomEvents 85706 * @method initEvents 85707 */ 85708 initEvents : function() { 85709 85710 var me = this, 85711 strEvent = "Event", 85712 CE = YAHOO.util.CustomEvent; 85713 85714 /** 85715 * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents 85716 * @method sub 85717 * @private 85718 * @param {Function} fn The function to subscribe to this CustomEvent 85719 * @param {Object} obj The CustomEvent's scope object 85720 * @param {Boolean} bOverride Whether or not to apply scope correction 85721 */ 85722 var sub = function(fn, obj, bOverride) { 85723 for (var p=0;p<me.pages.length;++p) { 85724 var cal = me.pages[p]; 85725 cal[this.type + strEvent].subscribe(fn, obj, bOverride); 85726 } 85727 }; 85728 85729 /** 85730 * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents 85731 * @method unsub 85732 * @private 85733 * @param {Function} fn The function to subscribe to this CustomEvent 85734 * @param {Object} obj The CustomEvent's scope object 85735 */ 85736 var unsub = function(fn, obj) { 85737 for (var p=0;p<me.pages.length;++p) { 85738 var cal = me.pages[p]; 85739 cal[this.type + strEvent].unsubscribe(fn, obj); 85740 } 85741 }; 85742 85743 var defEvents = Calendar._EVENT_TYPES; 85744 85745 /** 85746 * Fired before a date selection is made 85747 * @event beforeSelectEvent 85748 */ 85749 me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT); 85750 me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub; 85751 85752 /** 85753 * Fired when a date selection is made 85754 * @event selectEvent 85755 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD]. 85756 */ 85757 me.selectEvent = new CE(defEvents.SELECT); 85758 me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub; 85759 85760 /** 85761 * Fired before a date or set of dates is deselected 85762 * @event beforeDeselectEvent 85763 */ 85764 me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT); 85765 me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub; 85766 85767 /** 85768 * Fired when a date or set of dates has been deselected 85769 * @event deselectEvent 85770 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD]. 85771 */ 85772 me.deselectEvent = new CE(defEvents.DESELECT); 85773 me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub; 85774 85775 /** 85776 * Fired when the Calendar page is changed 85777 * @event changePageEvent 85778 */ 85779 me.changePageEvent = new CE(defEvents.CHANGE_PAGE); 85780 me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub; 85781 85782 /** 85783 * Fired before the Calendar is rendered 85784 * @event beforeRenderEvent 85785 */ 85786 me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER); 85787 me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub; 85788 85789 /** 85790 * Fired when the Calendar is rendered 85791 * @event renderEvent 85792 */ 85793 me.renderEvent = new CE(defEvents.RENDER); 85794 me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub; 85795 85796 /** 85797 * Fired when the Calendar is reset 85798 * @event resetEvent 85799 */ 85800 me.resetEvent = new CE(defEvents.RESET); 85801 me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub; 85802 85803 /** 85804 * Fired when the Calendar is cleared 85805 * @event clearEvent 85806 */ 85807 me.clearEvent = new CE(defEvents.CLEAR); 85808 me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub; 85809 85810 /** 85811 * Fired just before the CalendarGroup is to be shown 85812 * @event beforeShowEvent 85813 */ 85814 me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW); 85815 85816 /** 85817 * Fired after the CalendarGroup is shown 85818 * @event showEvent 85819 */ 85820 me.showEvent = new CE(defEvents.SHOW); 85821 85822 /** 85823 * Fired just before the CalendarGroup is to be hidden 85824 * @event beforeHideEvent 85825 */ 85826 me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE); 85827 85828 /** 85829 * Fired after the CalendarGroup is hidden 85830 * @event hideEvent 85831 */ 85832 me.hideEvent = new CE(defEvents.HIDE); 85833 85834 /** 85835 * Fired just before the CalendarNavigator is to be shown 85836 * @event beforeShowNavEvent 85837 */ 85838 me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV); 85839 85840 /** 85841 * Fired after the CalendarNavigator is shown 85842 * @event showNavEvent 85843 */ 85844 me.showNavEvent = new CE(defEvents.SHOW_NAV); 85845 85846 /** 85847 * Fired just before the CalendarNavigator is to be hidden 85848 * @event beforeHideNavEvent 85849 */ 85850 me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV); 85851 85852 /** 85853 * Fired after the CalendarNavigator is hidden 85854 * @event hideNavEvent 85855 */ 85856 me.hideNavEvent = new CE(defEvents.HIDE_NAV); 85857 85858 /** 85859 * Fired just before the CalendarNavigator is to be rendered 85860 * @event beforeRenderNavEvent 85861 */ 85862 me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV); 85863 85864 /** 85865 * Fired after the CalendarNavigator is rendered 85866 * @event renderNavEvent 85867 */ 85868 me.renderNavEvent = new CE(defEvents.RENDER_NAV); 85869 85870 /** 85871 * Fired just before the CalendarGroup is to be destroyed 85872 * @event beforeDestroyEvent 85873 */ 85874 me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY); 85875 85876 /** 85877 * Fired after the CalendarGroup is destroyed. This event should be used 85878 * for notification only. When this event is fired, important CalendarGroup instance 85879 * properties, dom references and event listeners have already been 85880 * removed/dereferenced, and hence the CalendarGroup instance is not in a usable 85881 * state. 85882 * 85883 * @event destroyEvent 85884 */ 85885 me.destroyEvent = new CE(defEvents.DESTROY); 85886 }, 85887 85888 /** 85889 * The default Config handler for the "pages" property 85890 * @method configPages 85891 * @param {String} type The CustomEvent type (usually the property name) 85892 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property. 85893 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner. 85894 */ 85895 configPages : function(type, args, obj) { 85896 var pageCount = args[0], 85897 cfgPageDate = DEF_CFG.PAGEDATE.key, 85898 sep = "_", 85899 caldate, 85900 firstPageDate = null, 85901 groupCalClass = "groupcal", 85902 firstClass = "first-of-type", 85903 lastClass = "last-of-type"; 85904 85905 for (var p=0;p<pageCount;++p) { 85906 var calId = this.id + sep + p, 85907 calContainerId = this.containerId + sep + p, 85908 childConfig = this.cfg.getConfig(); 85909 85910 childConfig.close = false;
85911 childConfig.title = false; 85912 childConfig.navigator = null; 85913 85914 if (p > 0) { 85915 caldate = new Date(firstPageDate); 85916 this._setMonthOnDate(caldate, caldate.getMonth() + p); 85917 childConfig.pageDate = caldate; 85918 } 85919 85920 var cal = this.constructChild(calId, calContainerId, childConfig); 85921 85922 Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE); 85923 Dom.addClass(cal.oDomContainer, groupCalClass); 85924 85925 if (p===0) { 85926 firstPageDate = cal.cfg.getProperty(cfgPageDate); 85927 Dom.addClass(cal.oDomContainer, firstClass); 85928 } 85929 85930 if (p==(pageCount-1)) { 85931 Dom.addClass(cal.oDomContainer, lastClass); 85932 } 85933 85934 cal.parent = this; 85935 cal.index = p; 85936 85937 this.pages[this.pages.length] = cal; 85938 } 85939 }, 85940 85941 /** 85942 * The default Config handler for the "pagedate" property 85943 * @method configPageDate 85944 * @param {String} type The CustomEvent type (usually the property name) 85945 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property. 85946 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner. 85947 */ 85948 configPageDate : function(type, args, obj) { 85949 var val = args[0], 85950 firstPageDate; 85951 85952 var cfgPageDate = DEF_CFG.PAGEDATE.key; 85953 85954 for (var p=0;p<this.pages.length;++p) { 85955 var cal = this.pages[p]; 85956 if (p === 0) { 85957 firstPageDate = cal._parsePageDate(val); 85958 cal.cfg.setProperty(cfgPageDate, firstPageDate); 85959 } else { 85960 var pageDate = new Date(firstPageDate); 85961 this._setMonthOnDate(pageDate, pageDate.getMonth() + p); 85962 cal.cfg.setProperty(cfgPageDate, pageDate); 85963 } 85964 } 85965 }, 85966 85967 /** 85968 * The default Config handler for the CalendarGroup "selected" property 85969 * @method configSelected 85970 * @param {String} type The CustomEvent type (usually the property name) 85971 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property. 85972 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner. 85973 */ 85974 configSelected : function(type, args, obj) { 85975 var cfgSelected = DEF_CFG.SELECTED.key; 85976 this.delegateConfig(type, args, obj); 85977 var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : []; 85978 this.cfg.setProperty(cfgSelected, selected, true); 85979 }, 85980 85981 85982 /** 85983 * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children 85984 * @method delegateConfig 85985 * @param {String} type The CustomEvent type (usually the property name) 85986 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property. 85987 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner. 85988 */ 85989 delegateConfig : function(type, args, obj) { 85990 var val = args[0]; 85991 var cal; 85992 85993 for (var p=0;p<this.pages.length;p++) { 85994 cal = this.pages[p]; 85995 cal.cfg.setProperty(type, val); 85996 } 85997 }, 85998 85999 /** 86000 * Adds a function to all child Calendars within this CalendarGroup. 86001 * @method setChildFunction 86002 * @param {String} fnName The name of the function 86003 * @param {Function} fn The function to apply to each Calendar page object 86004 */ 86005 setChildFunction : function(fnName, fn) { 86006 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key); 86007 86008 for (var p=0;p<pageCount;++p) { 86009 this.pages[p][fnName] = fn; 86010 } 86011 }, 86012 86013 /** 86014 * Calls a function within all child Calendars within this CalendarGroup. 86015 * @method callChildFunction 86016 * @param {String} fnName The name of the function 86017 * @param {Array} args The arguments to pass to the function 86018 */ 86019 callChildFunction : function(fnName, args) { 86020 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key); 86021 86022 for (var p=0;p<pageCount;++p) { 86023 var page = this.pages[p]; 86024 if (page[fnName]) { 86025 var fn = page[fnName];
86026 fn.call(page, args); 86027 } 86028 } 86029 }, 86030 86031 /** 86032 * Constructs a child calendar. This method can be overridden if a subclassed version of the default 86033 * calendar is to be used. 86034 * @method constructChild 86035 * @param {String} id The id of the table element that will represent the calendar widget 86036 * @param {String} containerId The id of the container div element that will wrap the calendar table 86037 * @param {Object} config The configuration object containing the Calendar's arguments 86038 * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed 86039 */ 86040 constructChild : function(id,containerId,config) { 86041 var container = document.getElementById(containerId); 86042 if (! container) { 86043 container = document.createElement("div"); 86044 container.id = containerId; 86045 this.oDomContainer.appendChild(container); 86046 } 86047 return new Calendar(id,containerId,config); 86048 }, 86049 86050 /** 86051 * Sets the calendar group's month explicitly. This month will be set into the first 86052 * page of the multi-page calendar, and all other months will be iterated appropriately. 86053 * @method setMonth 86054 * @param {Number} month The numeric month, from 0 (January) to 11 (December) 86055 */ 86056 setMonth : function(month) { 86057 month = parseInt(month, 10); 86058 var currYear; 86059 86060 var cfgPageDate = DEF_CFG.PAGEDATE.key; 86061 86062 for (var p=0; p<this.pages.length; ++p) { 86063 var cal = this.pages[p]; 86064 var pageDate = cal.cfg.getProperty(cfgPageDate); 86065 if (p === 0) { 86066 currYear = pageDate.getFullYear(); 86067 } else { 86068 pageDate.setFullYear(currYear); 86069 } 86070 this._setMonthOnDate(pageDate, month+p); 86071 cal.cfg.setProperty(cfgPageDate, pageDate); 86072 } 86073 }, 86074 86075 /** 86076 * Sets the calendar group's year explicitly. This year will be set into the first 86077 * page of the multi-page calendar, and all other months will be iterated appropriately. 86078 * @method setYear 86079 * @param {Number} year The numeric 4-digit year 86080 */ 86081 setYear : function(year) { 86082 86083 var cfgPageDate = DEF_CFG.PAGEDATE.key; 86084 86085 year = parseInt(year, 10); 86086 for (var p=0;p<this.pages.length;++p) { 86087 var cal = this.pages[p]; 86088 var pageDate = cal.cfg.getProperty(cfgPageDate); 86089 86090 if ((pageDate.getMonth()+1) == 1 && p>0) { 86091 year+=1; 86092 } 86093 cal.setYear(year); 86094 } 86095 }, 86096 86097 /** 86098 * Calls the render function of all child calendars within the group. 86099 * @method render 86100 */ 86101 render : function() { 86102 this.renderHeader(); 86103 for (var p=0;p<this.pages.length;++p) { 86104 var cal = this.pages[p]; 86105 cal.render(); 86106 } 86107 this.renderFooter(); 86108 }, 86109 86110 /** 86111 * Selects a date or a collection of dates on the current calendar. This method, by default, 86112 * does not call the render method explicitly. Once selection has completed, render must be 86113 * called for the changes to be reflected visually. 86114 * @method select 86115 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are 86116 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006). 86117 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005). 86118 * This method can also take a JavaScript Date object or an array of Date objects. 86119 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 86120 */ 86121 select : function(date) { 86122 for (var p=0;p<this.pages.length;++p) { 86123 var cal = this.pages[p]; 86124 cal.select(date); 86125 } 86126 return this.getSelectedDates(); 86127 }, 86128 86129 /** 86130 * Selects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly. 86131 * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected. 86132 * <ul> 86133 * <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li> 86134 * <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li> 86135 * </ul> 86136 * @method selectCell 86137 * @param {Number} cellIndex The index of the cell to be selected. 86138 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 86139 */ 86140 selectCell : function(cellIndex) { 86141 for (var p=0;p<this.pages.length;++p) { 86142 var cal = this.pages[p]; 86143 cal.selectCell(cellIndex); 86144 }
86145 return this.getSelectedDates(); 86146 }, 86147 86148 /** 86149 * Deselects a date or a collection of dates on the current calendar. This method, by default, 86150 * does not call the render method explicitly. Once deselection has completed, render must be 86151 * called for the changes to be reflected visually. 86152 * @method deselect 86153 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are 86154 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006). 86155 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005). 86156 * This method can also take a JavaScript Date object or an array of Date objects. 86157 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 86158 */ 86159 deselect : function(date) { 86160 for (var p=0;p<this.pages.length;++p) { 86161 var cal = this.pages[p]; 86162 cal.deselect(date); 86163 } 86164 return this.getSelectedDates(); 86165 }, 86166 86167 /** 86168 * Deselects all dates on the current calendar. 86169 * @method deselectAll 86170 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 86171 * Assuming that this function executes properly, the return value should be an empty array. 86172 * However, the empty array is returned for the sake of being able to check the selection status 86173 * of the calendar. 86174 */ 86175 deselectAll : function() { 86176 for (var p=0;p<this.pages.length;++p) { 86177 var cal = this.pages[p]; 86178 cal.deselectAll(); 86179 } 86180 return this.getSelectedDates(); 86181 }, 86182 86183 /** 86184 * Deselects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly. 86185 * deselectCell will deselect the cell at the specified index on each displayed Calendar page. 86186 * 86187 * @method deselectCell 86188 * @param {Number} cellIndex The index of the cell to deselect. 86189 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected. 86190 */ 86191 deselectCell : function(cellIndex) { 86192 for (var p=0;p<this.pages.length;++p) { 86193 var cal = this.pages[p]; 86194 cal.deselectCell(cellIndex); 86195 } 86196 return this.getSelectedDates(); 86197 }, 86198 86199 /** 86200 * Resets the calendar widget to the originally selected month and year, and 86201 * sets the calendar to the initial selection(s). 86202 * @method reset 86203 */ 86204 reset : function() { 86205 for (var p=0;p<this.pages.length;++p) { 86206 var cal = this.pages[p]; 86207 cal.reset(); 86208 } 86209 }, 86210 86211 /** 86212 * Clears the selected dates in the current calendar widget and sets the calendar 86213 * to the current month and year. 86214 * @method clear 86215 */ 86216 clear : function() { 86217 for (var p=0;p<this.pages.length;++p) { 86218 var cal = this.pages[p]; 86219 cal.clear(); 86220 } 86221 86222 this.cfg.setProperty(DEF_CFG.SELECTED.key, []); 86223 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime())); 86224 this.render(); 86225 }, 86226 86227 /** 86228 * Navigates to the next month page in the calendar widget. 86229 * @method nextMonth 86230 */ 86231 nextMonth : function() { 86232 for (var p=0;p<this.pages.length;++p) { 86233 var cal = this.pages[p]; 86234 cal.nextMonth(); 86235 } 86236 }, 86237 86238 /** 86239 * Navigates to the previous month page in the calendar widget. 86240 * @method previousMonth 86241 */ 86242 previousMonth : function() { 86243 for (var p=this.pages.length-1;p>=0;--p) { 86244 var cal = this.pages[p]; 86245 cal.previousMonth(); 86246 } 86247 }, 86248 86249 /** 86250 * Navigates to the next year in the currently selected month in the calendar widget. 86251 * @method nextYear 86252 */ 86253 nextYear : function() { 86254 for (var p=0;p<this.pages.length;++p) { 86255 var cal = this.pages[p]; 86256 cal.nextYear(); 86257 } 86258 }, 86259 86260 /** 86261 * Navigates to the previous year in the currently selected month in the calendar widget. 86262 * @method previousYear 86263 */ 86264 previousYear : function() { 86265 for (var p=0;p<this.pages.length;++p) { 86266 var cal = this.pages[p]; 86267 cal.previousYear(); 86268 } 86269 }, 86270 86271 /** 86272 * Gets the list of currently selected dates from the calendar. 86273 * @return An array of currently selected JavaScript Date objects. 86274 * @type Date[] 86275 */ 86276 getSelectedDates : function() { 86277 var returnDates = []; 86278 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key); 86279 for (var d=0;d<selected.length;++d) { 86280 var dateArray = selected[d]; 86281 86282 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]); 86283 returnDates.push(date); 86284 } 86285 86286 returnDates.sort( function(a,b) { return a-b; } ); 86287 return returnDates; 86288 }, 86289 86290 /** 86291 * Adds a renderer to the render stack. The function reference passed to this method will be executed 86292 * when a date cell matches the conditions specified in the date string for this renderer. 86293 * 86294 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 86295 * escape markup used to set the cell contents, if coming from an external source.<p> 86296 * @method addRenderer 86297 * @param {String} sDates A date string to associate with the specified renderer. Valid formats 86298 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005) 86299 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 86300 */ 86301 addRenderer : function(sDates, fnRender) { 86302 for (var p=0;p<this.pages.length;++p) { 86303 var cal = this.pages[p]; 86304 cal.addRenderer(sDates, fnRender); 86305 } 86306 }, 86307 86308 /** 86309 * Adds a month renderer to the render stack. The function reference passed to this method will be executed 86310 * when a date cell matches the month passed to this method 86311 * 86312 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 86313 * escape markup used to set the cell contents, if coming from an external source.<p> 86314 * @method addMonthRenderer 86315 * @param {Number} month The month (1-12) to associate with this renderer 86316 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 86317 */ 86318 addMonthRenderer : function(month, fnRender) { 86319 for (var p=0;p<this.pages.length;++p) { 86320 var cal = this.pages[p]; 86321 cal.addMonthRenderer(month, fnRender); 86322 } 86323 }, 86324 86325 /** 86326 * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed 86327 * when a date cell matches the weekday passed to this method. 86328 * 86329 * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 86330 * escape HTML used to set the cell contents, if coming from an external source.<p> 86331 * 86332 * @method addWeekdayRenderer 86333 * @param {Number} weekday The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer 86334 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer. 86335 */
86336 addWeekdayRenderer : function(weekday, fnRender) { 86337 for (var p=0;p<this.pages.length;++p) { 86338 var cal = this.pages[p]; 86339 cal.addWeekdayRenderer(weekday, fnRender); 86340 } 86341 }, 86342 86343 /** 86344 * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and 86345 * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers 86346 * to see the changes applied. 86347 * 86348 * @method removeRenderers 86349 */ 86350 removeRenderers : function() { 86351 this.callChildFunction("removeRenderers"); 86352 }, 86353 86354 /** 86355 * Renders the header for the CalendarGroup. 86356 * @method renderHeader 86357 */ 86358 renderHeader : function() { 86359 // EMPTY DEFAULT IMPL 86360 }, 86361 86362 /** 86363 * Renders a footer for the 2-up calendar container. By default, this method is 86364 * unimplemented. 86365 * @method renderFooter 86366 */ 86367 renderFooter : function() { 86368 // EMPTY DEFAULT IMPL 86369 }, 86370 86371 /** 86372 * Adds the designated number of months to the current calendar month, and sets the current 86373 * calendar page date to the new month. 86374 * @method addMonths 86375 * @param {Number} count The number of months to add to the current calendar 86376 */ 86377 addMonths : function(count) { 86378 this.callChildFunction("addMonths", count); 86379 }, 86380 86381 /** 86382 * Subtracts the designated number of months from the current calendar month, and sets the current 86383 * calendar page date to the new month. 86384 * @method subtractMonths 86385 * @param {Number} count The number of months to subtract from the current calendar 86386 */ 86387 subtractMonths : function(count) { 86388 this.callChildFunction("subtractMonths", count); 86389 }, 86390 86391 /** 86392 * Adds the designated number of years to the current calendar, and sets the current 86393 * calendar page date to the new month. 86394 * @method addYears 86395 * @param {Number} count The number of years to add to the current calendar 86396 */ 86397 addYears : function(count) { 86398 this.callChildFunction("addYears", count); 86399 }, 86400 86401 /** 86402 * Subtcats the designated number of years from the current calendar, and sets the current 86403 * calendar page date to the new month. 86404 * @method subtractYears 86405 * @param {Number} count The number of years to subtract from the current calendar 86406 */ 86407 subtractYears : function(count) { 86408 this.callChildFunction("subtractYears", count); 86409 }, 86410 86411 /** 86412 * Returns the Calendar page instance which has a pagedate (month/year) matching the given date. 86413 * Returns null if no match is found. 86414 * 86415 * @method getCalendarPage 86416 * @param {Date} date The JavaScript Date object for which a Calendar page is to be found. 86417 * @return {Calendar} The Calendar page instance representing the month to which the date 86418 * belongs. 86419 */ 86420 getCalendarPage : function(date) { 86421 var cal = null; 86422 if (date) { 86423 var y = date.getFullYear(), 86424 m = date.getMonth(); 86425 86426 var pages = this.pages; 86427 for (var i = 0; i < pages.length; ++i) { 86428 var pageDate = pages[i].cfg.getProperty("pagedate"); 86429 if (pageDate.getFullYear() === y && pageDate.getMonth() === m) { 86430 cal = pages[i]; 86431 break; 86432 } 86433 } 86434 } 86435 return cal; 86436 }, 86437 86438 /** 86439 * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11. 86440 * The Date object passed in is modified. It should be cloned before passing it into this method if the original value needs to be maintained 86441 * @method _setMonthOnDate 86442 * @private 86443 * @param {Date} date The Date object on which to set the month index 86444 * @param {Number} iMonth The month index to set 86445 */ 86446 _setMonthOnDate : function(date, iMonth) { 86447 // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11 86448 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) { 86449 var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth()); 86450 date.setTime(newDate.getTime()); 86451 } else { 86452 date.setMonth(iMonth); 86453 } 86454 }, 86455 86456 /** 86457 * Fixes the width of the CalendarGroup container element, to account for miswrapped floats 86458 * @method _fixWidth 86459 * @private 86460 */ 86461 _fixWidth : function() { 86462 var w = 0; 86463 for (var p=0;p<this.pages.length;++p) { 86464 var cal = this.pages[p]; 86465 w += cal.oDomContainer.offsetWidth; 86466 } 86467 if (w > 0) { 86468 this.oDomContainer.style.width = w + "px"; 86469 } 86470 }, 86471 86472 /** 86473 * Returns a string representation of the object. 86474 * @method toString 86475 * @return {String} A string representation of the CalendarGroup object. 86476 */ 86477 toString : function() { 86478 return "CalendarGroup " + this.id; 86479 }, 86480 86481 /** 86482 * Destroys the CalendarGroup instance. The method will remove references 86483 * to HTML elements, remove any event listeners added by the CalendarGroup. 86484 * 86485 * It will also destroy the Config and CalendarNavigator instances created by the 86486 * CalendarGroup and the individual Calendar instances created for each page. 86487 * 86488 * @method destroy 86489 */ 86490 destroy : function() { 86491 86492 if (this.beforeDestroyEvent.fire()) { 86493 86494 var cal = this; 86495 86496 // Child objects 86497 if (cal.navigator) { 86498 cal.navigator.destroy(); 86499 } 86500 86501 if (cal.cfg) { 86502 cal.cfg.destroy(); 86503 } 86504 86505 // DOM event listeners 86506 Event.purgeElement(cal.oDomContainer, true); 86507 86508 // Generated markup/DOM - Not removing the container DIV since we didn't create it. 86509 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER); 86510 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP); 86511 86512 for (var i = 0, l = cal.pages.length; i < l; i++) { 86513 cal.pages[i].destroy(); 86514 cal.pages[i] = null; 86515 } 86516 86517 cal.oDomContainer.innerHTML = ""; 86518 86519 // JS-to-DOM references 86520 cal.oDomContainer = null; 86521 86522 this.destroyEvent.fire(); 86523 } 86524 } 86525}; 86526 86527/** 86528* CSS class representing the container for the calendar 86529* @property YAHOO.widget.CalendarGroup.CSS_CONTAINER 86530* @static 86531* @final
86532* @type String 86533*/ 86534CalendarGroup.CSS_CONTAINER = "yui-calcontainer"; 86535 86536/** 86537* CSS class representing the container for the calendar 86538* @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP 86539* @static 86540* @final 86541* @type String 86542*/ 86543CalendarGroup.CSS_MULTI_UP = "multi"; 86544 86545/** 86546* CSS class representing the title for the 2-up calendar 86547* @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE 86548* @static 86549* @final 86550* @type String 86551*/ 86552CalendarGroup.CSS_2UPTITLE = "title"; 86553 86554/** 86555* CSS class representing the close icon for the 2-up calendar 86556* @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE 86557* @static 86558* @final 86559* @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties. 86560* Calendar's <a href="YAHOO.widget.Calendar.html#Style.CSS_CLOSE">Style.CSS_CLOSE</a> property now represents the CSS class used to render the close icon 86561* @type String 86562*/ 86563CalendarGroup.CSS_2UPCLOSE = "close-icon"; 86564 86565YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel", 86566 "buildMonthLabel", 86567 "renderOutOfBoundsDate", 86568 "renderRowHeader", 86569 "renderRowFooter", 86570 "renderCellDefault", 86571 "styleCellDefault", 86572 "renderCellStyleHighlight1", 86573 "renderCellStyleHighlight2", 86574 "renderCellStyleHighlight3", 86575 "renderCellStyleHighlight4", 86576 "renderCellStyleToday", 86577 "renderCellStyleSelected", 86578 "renderCellNotThisMonth", 86579 "styleCellNotThisMonth", 86580 "renderBodyCellRestricted", 86581 "initStyles", 86582 "configTitle", 86583 "configClose", 86584 "configIframe", 86585 "configStrings", 86586 "configToday", 86587 "configNavigator", 86588 "createTitleBar", 86589 "createCloseButton", 86590 "removeTitleBar", 86591 "removeCloseButton", 86592 "hide", 86593 "show", 86594 "toDate", 86595 "_toDate", 86596 "_parseArgs", 86597 "browser"); 86598 86599YAHOO.widget.CalGrp = CalendarGroup; 86600YAHOO.widget.CalendarGroup = CalendarGroup; 86601 86602/** 86603* @class YAHOO.widget.Calendar2up 86604* @extends YAHOO.widget.CalendarGroup 86605* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default. 86606*/ 86607YAHOO.widget.Calendar2up = function(id, containerId, config) { 86608 this.init(id, containerId, config); 86609}; 86610 86611YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup); 86612 86613/** 86614* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default. 86615*/ 86616YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up; 86617 86618})(); 86619/** 86620 * The CalendarNavigator is used along with a Calendar/CalendarGroup to 86621 * provide a Month/Year popup navigation control, allowing the user to navigate 86622 * to a specific month/year in the Calendar/CalendarGroup without having to 86623 * scroll through months sequentially 86624 * 86625 * @namespace YAHOO.widget 86626 * @class CalendarNavigator 86627 * @constructor 86628 * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
86629 */ 86630YAHOO.widget.CalendarNavigator = function(cal) { 86631 this.init(cal); 86632}; 86633 86634(function() { 86635 // Setup static properties (inside anon fn, so that we can use shortcuts) 86636 var CN = YAHOO.widget.CalendarNavigator; 86637 86638 /** 86639 * YAHOO.widget.CalendarNavigator.CLASSES contains constants 86640 * for the class values applied to the CalendarNaviatgator's 86641 * DOM elements 86642 * @property YAHOO.widget.CalendarNavigator.CLASSES 86643 * @type Object 86644 * @static 86645 */ 86646 CN.CLASSES = { 86647 /** 86648 * Class applied to the Calendar Navigator's bounding box 86649 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV 86650 * @type String 86651 * @static 86652 */ 86653 NAV :"yui-cal-nav", 86654 /** 86655 * Class applied to the Calendar/CalendarGroup's bounding box to indicate 86656 * the Navigator is currently visible 86657 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE 86658 * @type String 86659 * @static 86660 */ 86661 NAV_VISIBLE: "yui-cal-nav-visible", 86662 /** 86663 * Class applied to the Navigator mask's bounding box 86664 * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
86665 * @type String 86666 * @static 86667 */ 86668 MASK : "yui-cal-nav-mask", 86669 /** 86670 * Class applied to the year label/control bounding box 86671 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR 86672 * @type String 86673 * @static 86674 */ 86675 YEAR : "yui-cal-nav-y", 86676 /** 86677 * Class applied to the month label/control bounding box 86678 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH 86679 * @type String 86680 * @static 86681 */ 86682 MONTH : "yui-cal-nav-m", 86683 /** 86684 * Class applied to the submit/cancel button's bounding box 86685 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS 86686 * @type String 86687 * @static 86688 */ 86689 BUTTONS : "yui-cal-nav-b", 86690 /** 86691 * Class applied to buttons wrapping element 86692 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON 86693 * @type String 86694 * @static 86695 */ 86696 BUTTON : "yui-cal-nav-btn", 86697 /** 86698 * Class applied to the validation error area's bounding box 86699 * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
86700 * @type String 86701 * @static 86702 */ 86703 ERROR : "yui-cal-nav-e", 86704 /** 86705 * Class applied to the year input control 86706 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL 86707 * @type String 86708 * @static 86709 */ 86710 YEAR_CTRL : "yui-cal-nav-yc", 86711 /** 86712 * Class applied to the month input control 86713 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL 86714 * @type String 86715 * @static 86716 */ 86717 MONTH_CTRL : "yui-cal-nav-mc", 86718 /** 86719 * Class applied to controls with invalid data (e.g. a year input field with invalid an year) 86720 * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID 86721 * @type String 86722 * @static 86723 */ 86724 INVALID : "yui-invalid", 86725 /** 86726 * Class applied to default controls 86727 * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT 86728 * @type String 86729 * @static 86730 */ 86731 DEFAULT : "yui-default" 86732 }; 86733 86734 /** 86735 * Object literal containing the default configuration values for the CalendarNavigator 86736 * The configuration object is expected to follow the format below, with the properties being 86737 * case sensitive. 86738 * <dl> 86739 * <dt>strings</dt> 86740 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI 86741 * <dl> 86742 * <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd> 86743 * <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd> 86744 * <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd> 86745 * <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd> 86746 * <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd> 86747 * </dl> 86748 * </dd> 86749 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd> 86750 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd> 86751 * </dl> 86752 * @property DEFAULT_CONFIG 86753 * @type Object 86754 * @static 86755 */ 86756 CN.DEFAULT_CONFIG = { 86757 strings : { 86758 month: "Month", 86759 year: "Year", 86760 submit: "Okay", 86761 cancel: "Cancel", 86762 invalidYear : "Year needs to be a number" 86763 }, 86764 monthFormat: YAHOO.widget.Calendar.LONG, 86765 initialFocus: "year" 86766 }; 86767 86768 /** 86769 * Object literal containing the default configuration values for the CalendarNavigator 86770 * @property _DEFAULT_CFG 86771 * @protected 86772 * @deprecated Made public. See the public DEFAULT_CONFIG property 86773 * @type Object 86774 * @static 86775 */ 86776 CN._DEFAULT_CFG = CN.DEFAULT_CONFIG; 86777 86778 86779 /** 86780 * The suffix added to the Calendar/CalendarGroup's ID, to generate 86781 * a unique ID for the Navigator and it's bounding box. 86782 * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX 86783 * @static
86784 * @type String 86785 * @final 86786 */ 86787 CN.ID_SUFFIX = "_nav"; 86788 /** 86789 * The suffix added to the Navigator's ID, to generate 86790 * a unique ID for the month control. 86791 * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX 86792 * @static 86793 * @type String 86794 * @final 86795 */ 86796 CN.MONTH_SUFFIX = "_month"; 86797 /** 86798 * The suffix added to the Navigator's ID, to generate 86799 * a unique ID for the year control. 86800 * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX 86801 * @static 86802 * @type String 86803 * @final 86804 */ 86805 CN.YEAR_SUFFIX = "_year"; 86806 /** 86807 * The suffix added to the Navigator's ID, to generate 86808 * a unique ID for the error bounding box. 86809 * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX 86810 * @static 86811 * @type String 86812 * @final 86813 */ 86814 CN.ERROR_SUFFIX = "_error"; 86815 /** 86816 * The suffix added to the Navigator's ID, to generate 86817 * a unique ID for the "Cancel" button. 86818 * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX 86819 * @static
86820 * @type String 86821 * @final 86822 */ 86823 CN.CANCEL_SUFFIX = "_cancel"; 86824 /** 86825 * The suffix added to the Navigator's ID, to generate 86826 * a unique ID for the "Submit" button. 86827 * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX 86828 * @static 86829 * @type String 86830 * @final 86831 */ 86832 CN.SUBMIT_SUFFIX = "_submit"; 86833 86834 /** 86835 * The number of digits to which the year input control is to be limited. 86836 * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS 86837 * @static 86838 * @type Number 86839 */ 86840 CN.YR_MAX_DIGITS = 4; 86841 86842 /** 86843 * The amount by which to increment the current year value, 86844 * when the arrow up/down key is pressed on the year control 86845 * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC 86846 * @static 86847 * @type Number 86848 */ 86849 CN.YR_MINOR_INC = 1; 86850 86851 /** 86852 * The amount by which to increment the current year value, 86853 * when the page up/down key is pressed on the year control 86854 * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC 86855 * @static 86856 * @type Number 86857 */ 86858 CN.YR_MAJOR_INC = 10; 86859 86860 /** 86861 * Artificial delay (in ms) between the time the Navigator is hidden 86862 * and the Calendar/CalendarGroup state is updated. Allows the user 86863 * the see the Calendar/CalendarGroup page changing. If set to 0 86864 * the Calendar/CalendarGroup page will be updated instantly 86865 * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY 86866 * @static 86867 * @type Number 86868 */ 86869 CN.UPDATE_DELAY = 50; 86870 86871 /** 86872 * Regular expression used to validate the year input 86873 * @property YAHOO.widget.CalendarNavigator.YR_PATTERN 86874 * @static 86875 * @type RegExp 86876 */ 86877 CN.YR_PATTERN = /^\d+$/; 86878 /** 86879 * Regular expression used to trim strings 86880 * @property YAHOO.widget.CalendarNavigator.TRIM 86881 * @static 86882 * @type RegExp 86883 */ 86884 CN.TRIM = /^\s*(.*?)\s*$/; 86885})(); 86886 86887YAHOO.widget.CalendarNavigator.prototype = { 86888 86889 /** 86890 * The unique ID for this CalendarNavigator instance 86891 * @property id
86892 * @type String 86893 */ 86894 id : null, 86895 86896 /** 86897 * The Calendar/CalendarGroup instance to which the navigator belongs 86898 * @property cal 86899 * @type {Calendar|CalendarGroup} 86900 */ 86901 cal : null, 86902 86903 /** 86904 * Reference to the HTMLElement used to render the navigator's bounding box 86905 * @property navEl 86906 * @type HTMLElement 86907 */ 86908 navEl : null, 86909 86910 /** 86911 * Reference to the HTMLElement used to render the navigator's mask 86912 * @property maskEl 86913 * @type HTMLElement 86914 */ 86915 maskEl : null, 86916 86917 /** 86918 * Reference to the HTMLElement used to input the year 86919 * @property yearEl 86920 * @type HTMLElement 86921 */ 86922 yearEl : null, 86923 86924 /** 86925 * Reference to the HTMLElement used to input the month 86926 * @property monthEl 86927 * @type HTMLElement 86928 */ 86929 monthEl : null, 86930 86931 /** 86932 * Reference to the HTMLElement used to display validation errors 86933 * @property errorEl 86934 * @type HTMLElement 86935 */ 86936 errorEl : null, 86937 86938 /** 86939 * Reference to the HTMLElement used to update the Calendar/Calendar group 86940 * with the month/year values 86941 * @property submitEl 86942 * @type HTMLElement 86943 */ 86944 submitEl : null, 86945 86946 /** 86947 * Reference to the HTMLElement used to hide the navigator without updating the 86948 * Calendar/Calendar group 86949 * @property cancelEl 86950 * @type HTMLElement 86951 */ 86952 cancelEl : null, 86953 86954 /** 86955 * Reference to the first focusable control in the navigator (by default monthEl) 86956 * @property firstCtrl 86957 * @type HTMLElement 86958 */ 86959 firstCtrl : null, 86960 86961 /** 86962 * Reference to the last focusable control in the navigator (by default cancelEl) 86963 * @property lastCtrl 86964 * @type HTMLElement 86965 */ 86966 lastCtrl : null, 86967 86968 /** 86969 * The document containing the Calendar/Calendar group instance 86970 * @protected 86971 * @property _doc 86972 * @type HTMLDocument 86973 */ 86974 _doc : null, 86975 86976 /** 86977 * Internal state property for the current year displayed in the navigator 86978 * @protected 86979 * @property _year 86980 * @type Number 86981 */ 86982 _year: null, 86983 86984 /** 86985 * Internal state property for the current month index displayed in the navigator 86986 * @protected 86987 * @property _month 86988 * @type Number 86989 */ 86990 _month: 0, 86991 86992 /** 86993 * Private internal state property which indicates whether or not the 86994 * Navigator has been rendered. 86995 * @private 86996 * @property __rendered 86997 * @type Boolean 86998 */ 86999 __rendered: false, 87000 87001 /** 87002 * Init lifecycle method called as part of construction 87003 * 87004 * @method init 87005 * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached 87006 */ 87007 init : function(cal) { 87008 var calBox = cal.oDomContainer; 87009 87010 this.cal = cal; 87011 this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX; 87012 this._doc = calBox.ownerDocument; 87013 87014 /** 87015 * Private flag, to identify IE Quirks 87016 * @private 87017 * @property __isIEQuirks 87018 */ 87019 var ie = YAHOO.env.ua.ie; 87020 this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat"))); 87021 }, 87022 87023 /** 87024 * Displays the navigator and mask, updating the input controls to reflect the 87025 * currently set month and year. The show method will invoke the render method 87026 * if the navigator has not been renderered already, allowing for lazy rendering 87027 * of the control. 87028 * 87029 * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events 87030 * 87031 * @method show 87032 */ 87033 show : function() { 87034 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES; 87035 87036 if (this.cal.beforeShowNavEvent.fire()) { 87037 if (!this.__rendered) { 87038 this.render(); 87039 } 87040 this.clearErrors(); 87041 87042 this._updateMonthUI(); 87043 this._updateYearUI(); 87044 this._show(this.navEl, true); 87045 87046 this.setInitialFocus(); 87047 this.showMask(); 87048 87049 YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE); 87050 this.cal.showNavEvent.fire(); 87051 } 87052 }, 87053 87054 /** 87055 * Hides the navigator and mask 87056 * 87057 * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events 87058 * @method hide 87059 */ 87060 hide : function() { 87061 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES; 87062 87063 if (this.cal.beforeHideNavEvent.fire()) { 87064 this._show(this.navEl, false); 87065 this.hideMask(); 87066 YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE); 87067 this.cal.hideNavEvent.fire(); 87068 } 87069 }, 87070 87071 87072 /**
87073 * Displays the navigator's mask element 87074 * 87075 * @method showMask 87076 */ 87077 showMask : function() { 87078 this._show(this.maskEl, true); 87079 if (this.__isIEQuirks) { 87080 this._syncMask(); 87081 } 87082 }, 87083 87084 /** 87085 * Hides the navigator's mask element 87086 * 87087 * @method hideMask 87088 */ 87089 hideMask : function() { 87090 this._show(this.maskEl, false); 87091 }, 87092 87093 /** 87094 * Returns the current month set on the navigator 87095 * 87096 * Note: This may not be the month set in the UI, if 87097 * the UI contains an invalid value. 87098 * 87099 * @method getMonth 87100 * @return {Number} The Navigator's current month index 87101 */ 87102 getMonth: function() { 87103 return this._month; 87104 }, 87105 87106 /** 87107 * Returns the current year set on the navigator 87108 * 87109 * Note: This may not be the year set in the UI, if 87110 * the UI contains an invalid value. 87111 * 87112 * @method getYear 87113 * @return {Number} The Navigator's current year value 87114 */ 87115 getYear: function() { 87116 return this._year; 87117 }, 87118 87119 /** 87120 * Sets the current month on the Navigator, and updates the UI 87121 * 87122 * @method setMonth 87123 * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec). 87124 */ 87125 setMonth : function(nMonth) { 87126 if (nMonth >= 0 && nMonth < 12) { 87127 this._month = nMonth; 87128 } 87129 this._updateMonthUI(); 87130 }, 87131 87132 /** 87133 * Sets the current year on the Navigator, and updates the UI. If the 87134 * provided year is invalid, it will not be set. 87135 * 87136 * @method setYear 87137 * @param {Number} nYear The full year value to set the Navigator to. 87138 */ 87139 setYear : function(nYear) { 87140 var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN; 87141 if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) { 87142 this._year = nYear; 87143 } 87144 this._updateYearUI(); 87145 }, 87146 87147 /** 87148 * Renders the HTML for the navigator, adding it to the 87149 * document and attaches event listeners if it has not 87150 * already been rendered. 87151 * 87152 * @method render 87153 */ 87154 render: function() { 87155 this.cal.beforeRenderNavEvent.fire(); 87156 if (!this.__rendered) { 87157 this.createNav(); 87158 this.createMask(); 87159 this.applyListeners(); 87160 this.__rendered = true; 87161 } 87162 this.cal.renderNavEvent.fire(); 87163 }, 87164 87165 /** 87166 * Creates the navigator's containing HTMLElement, it's contents, and appends 87167 * the containg element to the Calendar/CalendarGroup's container. 87168 * 87169 * @method createNav 87170 */ 87171 createNav : function() { 87172 var NAV = YAHOO.widget.CalendarNavigator; 87173 var doc = this._doc; 87174 87175 var d = doc.createElement("div"); 87176 d.className = NAV.CLASSES.NAV; 87177 87178 var htmlBuf = this.renderNavContents([]); 87179 87180 d.innerHTML = htmlBuf.join(''); 87181 this.cal.oDomContainer.appendChild(d); 87182 87183 this.navEl = d; 87184 87185 this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX); 87186 this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX); 87187 this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX); 87188 this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX); 87189 this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX); 87190 87191 if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") { 87192 // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791, 87193 // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ] 87194 this.yearEl.setAttribute("autocomplete", "off"); 87195 } 87196 87197 this._setFirstLastElements(); 87198 }, 87199 87200 /** 87201 * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups 87202 * container. 87203 * 87204 * @method createMask 87205 */ 87206 createMask : function() { 87207 var C = YAHOO.widget.CalendarNavigator.CLASSES; 87208 87209 var d = this._doc.createElement("div"); 87210 d.className = C.MASK; 87211 87212 this.cal.oDomContainer.appendChild(d); 87213 this.maskEl = d; 87214 }, 87215 87216 /** 87217 * Used to set the width/height of the mask in pixels to match the Calendar Container. 87218 * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%). 87219 * <p> 87220 * The method is also registered as an HTMLElement resize listener on the Calendars container element. 87221 * </p> 87222 * @protected 87223 * @method _syncMask 87224 */ 87225 _syncMask : function() { 87226 var c = this.cal.oDomContainer; 87227 if (c && this.maskEl) { 87228 var r = YAHOO.util.Dom.getRegion(c); 87229 YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px"); 87230 YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px"); 87231 } 87232 }, 87233 87234 /**
87235 * Renders the contents of the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 87236 * 87237 * @method renderNavContents 87238 * 87239 * @param {HTML[]} html The HTML buffer to append the HTML to. 87240 * @return {HTML[]} A reference to the buffer passed in. 87241 */ 87242 renderNavContents : function(html) { 87243 var NAV = YAHOO.widget.CalendarNavigator, 87244 C = NAV.CLASSES, 87245 h = html; // just to use a shorter name 87246 87247 h[h.length] = '<div class="' + C.MONTH + '">'; 87248 this.renderMonth(h); 87249 h[h.length] = '</div>'; 87250 h[h.length] = '<div class="' + C.YEAR + '">'; 87251 this.renderYear(h); 87252 h[h.length] = '</div>'; 87253 h[h.length] = '<div class="' + C.BUTTONS + '">'; 87254 this.renderButtons(h); 87255 h[h.length] = '</div>'; 87256 h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>'; 87257 87258 return h; 87259 }, 87260 87261 /** 87262 * Renders the month label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 87263 * 87264 * @method renderNavContents 87265 * @param {HTML[]} html The HTML buffer to append the HTML to. 87266 * @return {HTML[]} A reference to the buffer passed in. 87267 */ 87268 renderMonth : function(html) { 87269 var NAV = YAHOO.widget.CalendarNavigator, 87270 C = NAV.CLASSES; 87271 87272 var id = this.id + NAV.MONTH_SUFFIX, 87273 mf = this.__getCfg("monthFormat"), 87274 months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"), 87275 h = html; 87276 87277 if (months && months.length > 0) { 87278 h[h.length] = '<label for="' + id + '">'; 87279 h[h.length] = this.__getCfg("month", true); 87280 h[h.length] = '</label>'; 87281 h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">'; 87282 for (var i = 0; i < months.length; i++) { 87283 h[h.length] = '<option value="' + i + '">'; 87284 h[h.length] = months[i]; 87285 h[h.length] = '</option>'; 87286 } 87287 h[h.length] = '</select>'; 87288 } 87289 return h; 87290 }, 87291 87292 /** 87293 * Renders the year label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 87294 * 87295 * @method renderYear 87296 * @param {Array} html The HTML buffer to append the HTML to. 87297 * @return {Array} A reference to the buffer passed in. 87298 */ 87299 renderYear : function(html) { 87300 var NAV = YAHOO.widget.CalendarNavigator, 87301 C = NAV.CLASSES; 87302 87303 var id = this.id + NAV.YEAR_SUFFIX, 87304 size = NAV.YR_MAX_DIGITS, 87305 h = html; 87306 87307 h[h.length] = '<label for="' + id + '">'; 87308 h[h.length] = this.__getCfg("year", true); 87309 h[h.length] = '</label>'; 87310 h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>'; 87311 return h; 87312 }, 87313 87314 /** 87315 * Renders the submit/cancel buttons for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 87316 * 87317 * @method renderButtons 87318 * @param {Array} html The HTML buffer to append the HTML to. 87319 * @return {Array} A reference to the buffer passed in. 87320 */ 87321 renderButtons : function(html) { 87322 var C = YAHOO.widget.CalendarNavigator.CLASSES; 87323 var h = html; 87324 87325 h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">'; 87326 h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">'; 87327 h[h.length] = this.__getCfg("submit", true); 87328 h[h.length] = '</button>'; 87329 h[h.length] = '</span>'; 87330 h[h.length] = '<span class="' + C.BUTTON +'">'; 87331 h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">'; 87332 h[h.length] = this.__getCfg("cancel", true); 87333 h[h.length] = '</button>'; 87334 h[h.length] = '</span>'; 87335 87336 return h; 87337 }, 87338 87339 /** 87340 * Attaches DOM event listeners to the rendered elements 87341 * <p> 87342 * The method will call applyKeyListeners, to setup keyboard specific 87343 * listeners 87344 * </p> 87345 * @method applyListeners 87346 */ 87347 applyListeners : function() { 87348 var E = YAHOO.util.Event; 87349 87350 function yearUpdateHandler() { 87351 if (this.validate()) { 87352 this.setYear(this._getYearFromUI()); 87353 } 87354 } 87355 87356 function monthUpdateHandler() { 87357 this.setMonth(this._getMonthFromUI()); 87358 } 87359 87360 E.on(this.submitEl, "click", this.submit, this, true); 87361 E.on(this.cancelEl, "click", this.cancel, this, true); 87362 E.on(this.yearEl, "blur", yearUpdateHandler, this, true); 87363 E.on(this.monthEl, "change", monthUpdateHandler, this, true); 87364 87365 if (this.__isIEQuirks) { 87366 YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true); 87367 } 87368 87369 this.applyKeyListeners(); 87370 }, 87371 87372 /** 87373 * Removes/purges DOM event listeners from the rendered elements 87374 * 87375 * @method purgeListeners 87376 */ 87377 purgeListeners : function() { 87378 var E = YAHOO.util.Event; 87379 E.removeListener(this.submitEl, "click", this.submit); 87380 E.removeListener(this.cancelEl, "click", this.cancel); 87381 E.removeListener(this.yearEl, "blur"); 87382 E.removeListener(this.monthEl, "change"); 87383 if (this.__isIEQuirks) { 87384 E.removeListener(this.cal.oDomContainer, "resize", this._syncMask); 87385 } 87386 87387 this.purgeKeyListeners(); 87388 }, 87389 87390 /** 87391 * Attaches DOM listeners for keyboard support.
87392 * Tab/Shift-Tab looping, Enter Key Submit on Year element, 87393 * Up/Down/PgUp/PgDown year increment on Year element 87394 * <p> 87395 * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and 87396 * MacOSX Gecko does not let you tab to buttons or select controls, 87397 * so for these browsers, Tab/Shift-Tab looping is limited to the 87398 * elements which can be reached using the tab key. 87399 * </p> 87400 * @method applyKeyListeners 87401 */ 87402 applyKeyListeners : function() { 87403 var E = YAHOO.util.Event, 87404 ua = YAHOO.env.ua; 87405 87406 // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys) 87407 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress"; 87408 87409 // - IE/Safari 3.1 doesn't fire keypress for non-char keys 87410 // - Opera doesn't allow us to cancel keydown or keypress for tab, but 87411 // changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on). 87412 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress"; 87413 87414 // Everyone likes keypress for Enter (char keys) - whoo hoo! 87415 E.on(this.yearEl, "keypress", this._handleEnterKey, this, true); 87416 87417 E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true); 87418 E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true); 87419 E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true); 87420 }, 87421 87422 /** 87423 * Removes/purges DOM listeners for keyboard support 87424 * 87425 * @method purgeKeyListeners 87426 */ 87427 purgeKeyListeners : function() { 87428 var E = YAHOO.util.Event, 87429 ua = YAHOO.env.ua; 87430 87431 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress"; 87432 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress"; 87433 87434 E.removeListener(this.yearEl, "keypress", this._handleEnterKey); 87435 E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys); 87436 E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey); 87437 E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey); 87438 }, 87439 87440 /** 87441 * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid. 87442 * <p> 87443 * If the currently set month/year is invalid, a validation error will be displayed and the 87444 * Calendar/CalendarGroup's pagedate will not be updated. 87445 * </p> 87446 * @method submit 87447 */ 87448 submit : function() { 87449 if (this.validate()) { 87450 this.hide(); 87451 87452 this.setMonth(this._getMonthFromUI()); 87453 this.setYear(this._getYearFromUI()); 87454 87455 var cal = this.cal; 87456 87457 // Artificial delay, just to help the user see something changed 87458 var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY; 87459 if (delay > 0) { 87460 var nav = this; 87461 window.setTimeout(function(){ nav._update(cal); }, delay); 87462 } else { 87463 this._update(cal); 87464 } 87465 } 87466 }, 87467 87468 /** 87469 * Updates the Calendar rendered state, based on the state of the CalendarNavigator 87470 * @method _update 87471 * @param cal The Calendar instance to update 87472 * @protected 87473 */ 87474 _update : function(cal) { 87475 var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1); 87476 cal.cfg.setProperty("pagedate", date); 87477 cal.render(); 87478 }, 87479 87480 /** 87481 * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state 87482 * 87483 * @method cancel 87484 */ 87485 cancel : function() { 87486 this.hide(); 87487 }, 87488 87489 /** 87490 * Validates the current state of the UI controls 87491 * 87492 * @method validate 87493 * @return {Boolean} true, if the current UI state contains valid values, false if not 87494 */ 87495 validate : function() { 87496 if (this._getYearFromUI() !== null) { 87497 this.clearErrors(); 87498 return true; 87499 } else { 87500 this.setYearError(); 87501 this.setError(this.__getCfg("invalidYear", true)); 87502 return false; 87503 } 87504 }, 87505 87506 /**
87507 * Displays an error message in the Navigator's error panel. 87508 * 87509 * @method setError 87510 * @param {HTML} msg The markup for the error message to display. NOTE: The msg passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 87511 */ 87512 setError : function(msg) { 87513 if (this.errorEl) { 87514 this.errorEl.innerHTML = msg; 87515 this._show(this.errorEl, true); 87516 } 87517 }, 87518 87519 /** 87520 * Clears the navigator's error message and hides the error panel 87521 * @method clearError 87522 */ 87523 clearError : function() { 87524 if (this.errorEl) { 87525 this.errorEl.innerHTML = ""; 87526 this._show(this.errorEl, false); 87527 } 87528 }, 87529 87530 /** 87531 * Displays the validation error UI for the year control 87532 * @method setYearError 87533 */ 87534 setYearError : function() { 87535 YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID); 87536 }, 87537 87538 /** 87539 * Removes the validation error UI for the year control 87540 * @method clearYearError 87541 */ 87542 clearYearError : function() { 87543 YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID); 87544 }, 87545 87546 /** 87547 * Clears all validation and error messages in the UI 87548 * @method clearErrors 87549 */ 87550 clearErrors : function() { 87551 this.clearError(); 87552 this.clearYearError(); 87553 }, 87554 87555 /** 87556 * Sets the initial focus, based on the configured value 87557 * @method setInitialFocus 87558 */ 87559 setInitialFocus : function() { 87560 var el = this.submitEl, 87561 f = this.__getCfg("initialFocus"); 87562 87563 if (f && f.toLowerCase) { 87564 f = f.toLowerCase(); 87565 if (f == "year") { 87566 el = this.yearEl; 87567 try { 87568 this.yearEl.select(); 87569 } catch (selErr) { 87570 // Ignore; 87571 } 87572 } else if (f == "month") { 87573 el = this.monthEl; 87574 } 87575 } 87576 87577 if (el && YAHOO.lang.isFunction(el.focus)) { 87578 try { 87579 el.focus(); 87580 } catch (focusErr) { 87581 // TODO: Fall back if focus fails? 87582 } 87583 } 87584 }, 87585 87586 /** 87587 * Removes all renderered HTML elements for the Navigator from 87588 * the DOM, purges event listeners and clears (nulls) any property 87589 * references to HTML references 87590 * @method erase 87591 */ 87592 erase : function() { 87593 if (this.__rendered) { 87594 this.purgeListeners(); 87595 87596 // Clear out innerHTML references 87597 this.yearEl = null; 87598 this.monthEl = null; 87599 this.errorEl = null; 87600 this.submitEl = null; 87601 this.cancelEl = null; 87602 this.firstCtrl = null; 87603 this.lastCtrl = null; 87604 if (this.navEl) { 87605 this.navEl.innerHTML = ""; 87606 } 87607 87608 var p = this.navEl.parentNode; 87609 if (p) { 87610 p.removeChild(this.navEl); 87611 } 87612 this.navEl = null; 87613 87614 var pm = this.maskEl.parentNode; 87615 if (pm) { 87616 pm.removeChild(this.maskEl); 87617 } 87618 this.maskEl = null; 87619 this.__rendered = false; 87620 } 87621 }, 87622 87623 /** 87624 * Destroys the Navigator object and any HTML references 87625 * @method destroy 87626 */ 87627 destroy : function() { 87628 this.erase(); 87629 this._doc = null; 87630 this.cal = null; 87631 this.id = null; 87632 }, 87633 87634 /** 87635 * Protected implementation to handle how UI elements are 87636 * hidden/shown. 87637 * 87638 * @method _show 87639 * @protected 87640 */ 87641 _show : function(el, bShow) { 87642 if (el) { 87643 YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none"); 87644 } 87645 }, 87646 87647 /** 87648 * Returns the month value (index), from the month UI element 87649 * @protected 87650 * @method _getMonthFromUI 87651 * @return {Number} The month index, or 0 if a UI element for the month 87652 * is not found 87653 */ 87654 _getMonthFromUI : function() { 87655 if (this.monthEl) { 87656 return this.monthEl.selectedIndex; 87657 } else { 87658 return 0; // Default to Jan 87659 } 87660 }, 87661 87662 /** 87663 * Returns the year value, from the Navitator's year UI element 87664 * @protected 87665 * @method _getYearFromUI 87666 * @return {Number} The year value set in the UI, if valid. null is returned if 87667 * the UI does not contain a valid year value. 87668 */ 87669 _getYearFromUI : function() { 87670 var NAV = YAHOO.widget.CalendarNavigator; 87671 87672 var yr = null; 87673 if (this.yearEl) { 87674 var value = this.yearEl.value; 87675 value = value.replace(NAV.TRIM, "$1"); 87676 87677 if (NAV.YR_PATTERN.test(value)) { 87678 yr = parseInt(value, 10); 87679 } 87680 } 87681 return yr; 87682 }, 87683 87684 /** 87685 * Updates the Navigator's year UI, based on the year value set on the Navigator object 87686 * @protected 87687 * @method _updateYearUI 87688 */ 87689 _updateYearUI : function() { 87690 if (this.yearEl && this._year !== null) { 87691 this.yearEl.value = this._year; 87692 } 87693 }, 87694 87695 /** 87696 * Updates the Navigator's month UI, based on the month value set on the Navigator object 87697 * @protected 87698 * @method _updateMonthUI 87699 */ 87700 _updateMonthUI : function() { 87701 if (this.monthEl) { 87702 this.monthEl.selectedIndex = this._month; 87703 } 87704 }, 87705 87706 /** 87707 * Sets up references to the first and last focusable element in the Navigator's UI 87708 * in terms of tab order (Naviagator's firstEl and lastEl properties). The references 87709 * are used to control modality by looping around from the first to the last control 87710 * and visa versa for tab/shift-tab navigation. 87711 * <p> 87712 * See <a href="#applyKeyListeners">applyKeyListeners</a> 87713 * </p> 87714 * @protected 87715 * @method _setFirstLastElements 87716 */ 87717 _setFirstLastElements : function() { 87718 this.firstCtrl = this.monthEl; 87719 this.lastCtrl = this.cancelEl; 87720 87721 // Special handling for MacOSX. 87722 // - Safari 2.x can't focus on buttons 87723 // - Gecko can't focus on select boxes or buttons 87724 if (this.__isMac) { 87725 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){ 87726 this.firstCtrl = this.monthEl; 87727 this.lastCtrl = this.yearEl; 87728 } 87729 if (YAHOO.env.ua.gecko) { 87730 this.firstCtrl = this.yearEl; 87731 this.lastCtrl = this.yearEl; 87732 } 87733 } 87734 }, 87735 87736 /** 87737 * Default Keyboard event handler to capture Enter 87738 * on the Navigator's year control (yearEl) 87739 * 87740 * @method _handleEnterKey 87741 * @protected 87742 * @param {Event} e The DOM event being handled 87743 */ 87744 _handleEnterKey : function(e) { 87745 var KEYS = YAHOO.util.KeyListener.KEY; 87746 87747 if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) { 87748 YAHOO.util.Event.preventDefault(e); 87749 this.submit(); 87750 } 87751 }, 87752 87753 /** 87754 * Default Keyboard event handler to capture up/down/pgup/pgdown 87755 * on the Navigator's year control (yearEl). 87756 * 87757 * @method _handleDirectionKeys 87758 * @protected 87759 * @param {Event} e The DOM event being handled 87760 */ 87761 _handleDirectionKeys : function(e) { 87762 var E = YAHOO.util.Event, 87763 KEYS = YAHOO.util.KeyListener.KEY, 87764 NAV = YAHOO.widget.CalendarNavigator; 87765 87766 var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null; 87767 if (isFinite(value)) { 87768 var dir = false;
87769 switch(E.getCharCode(e)) { 87770 case KEYS.UP: 87771 this.yearEl.value = value + NAV.YR_MINOR_INC; 87772 dir = true; 87773 break; 87774 case KEYS.DOWN: 87775 this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0); 87776 dir = true; 87777 break; 87778 case KEYS.PAGE_UP: 87779 this.yearEl.value = value + NAV.YR_MAJOR_INC; 87780 dir = true; 87781 break; 87782 case KEYS.PAGE_DOWN: 87783 this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0); 87784 dir = true; 87785 break; 87786 default: 87787 break; 87788 } 87789 if (dir) { 87790 E.preventDefault(e); 87791 try { 87792 this.yearEl.select(); 87793 } catch(err) { 87794 // Ignore 87795 } 87796 } 87797 } 87798 }, 87799 87800 /** 87801 * Default Keyboard event handler to capture Tab 87802 * on the last control (lastCtrl) in the Navigator. 87803 * 87804 * @method _handleTabKey 87805 * @protected 87806 * @param {Event} e The DOM event being handled 87807 */ 87808 _handleTabKey : function(e) { 87809 var E = YAHOO.util.Event, 87810 KEYS = YAHOO.util.KeyListener.KEY; 87811 87812 if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) { 87813 try { 87814 E.preventDefault(e); 87815 this.firstCtrl.focus(); 87816 } catch (err) { 87817 // Ignore - mainly for focus edge cases 87818 } 87819 } 87820 }, 87821 87822 /** 87823 * Default Keyboard event handler to capture Shift-Tab 87824 * on the first control (firstCtrl) in the Navigator. 87825 * 87826 * @method _handleShiftTabKey 87827 * @protected 87828 * @param {Event} e The DOM event being handled 87829 */ 87830 _handleShiftTabKey : function(e) { 87831 var E = YAHOO.util.Event, 87832 KEYS = YAHOO.util.KeyListener.KEY; 87833 87834 if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) { 87835 try { 87836 E.preventDefault(e); 87837 this.lastCtrl.focus(); 87838 } catch (err) { 87839 // Ignore - mainly for focus edge cases 87840 } 87841 } 87842 }, 87843 87844 /** 87845 * Retrieve Navigator configuration values from 87846 * the parent Calendar/CalendarGroup's config value. 87847 * <p> 87848 * If it has not been set in the user provided configuration, the method will 87849 * return the default value of the configuration property, as set in DEFAULT_CONFIG 87850 * </p> 87851 * @private 87852 * @method __getCfg 87853 * @param {String} Case sensitive property name. 87854 * @param {Boolean} true, if the property is a string property, false if not. 87855 * @return The value of the configuration property 87856 */ 87857 __getCfg : function(prop, bIsStr) { 87858 var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG; 87859 var cfg = this.cal.cfg.getProperty("navigator"); 87860 87861 if (bIsStr) { 87862 return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop]; 87863 } else { 87864 return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop]; 87865 } 87866 }, 87867 87868 /** 87869 * Private flag, to identify MacOS 87870 * @private 87871 * @property __isMac 87872 */ 87873 __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1) 87874 87875}; 87876YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.9.0", build: "2800"}); 87877/* 87878Copyright (c) 2011, Yahoo! Inc. All rights reserved. 87879Code licensed under the BSD License: 87880http://developer.yahoo.com/yui/license.html 87881version: 2.9.0 87882*/ 87883/** 87884 * @description <p>Makes an element resizable</p> 87885 * @namespace YAHOO.util 87886 * @requires yahoo, dom, dragdrop, element, event 87887 * @optional animation 87888 * @module resize 87889 */ 87890(function() { 87891var D = YAHOO.util.Dom, 87892 Event = YAHOO.util.Event, 87893 Lang = YAHOO.lang; 87894 87895 /** 87896 * @constructor 87897 * @class Resize 87898 * @extends YAHOO.util.Element 87899 * @description <p>Makes an element resizable</p> 87900 * @param {String/HTMLElement} el The element to make resizable. 87901 * @param {Object} attrs Object liternal containing configuration parameters. 87902 */ 87903 87904 var Resize = function(el, config) { 87905 YAHOO.log('Creating Resize Object', 'info', 'Resize'); 87906 var oConfig = { 87907 element: el, 87908 attributes: config || {} 87909 }; 87910 87911 Resize.superclass.constructor.call(this, oConfig.element, oConfig.attributes); 87912 }; 87913 87914 /** 87915 * @private 87916 * @static 87917 * @property _instances
87918 * @description Internal hash table for all resize instances 87919 * @type Object 87920 */ 87921 Resize._instances = {}; 87922 /** 87923 * @static 87924 * @method getResizeById 87925 * @description Get's a resize object by the HTML id of the element associated with the Resize object. 87926 * @return {Object} The Resize Object 87927 */ 87928 Resize.getResizeById = function(id) { 87929 if (Resize._instances[id]) { 87930 return Resize._instances[id]; 87931 } 87932 YAHOO.log('No Instance Found', 'error', 'Resize'); 87933 return false; 87934 }; 87935 87936 YAHOO.extend(Resize, YAHOO.util.Element, { 87937 /** 87938 * @private 87939 * @property CSS_RESIZE 87940 * @description Base CSS class name 87941 * @type String 87942 */ 87943 CSS_RESIZE: 'yui-resize', 87944 /** 87945 * @private 87946 * @property CSS_DRAG 87947 * @description Class name added when dragging is enabled 87948 * @type String 87949 */ 87950 CSS_DRAG: 'yui-draggable', 87951 /** 87952 * @private 87953 * @property CSS_HOVER 87954 * @description Class name used for hover only handles
87955 * @type String 87956 */ 87957 CSS_HOVER: 'yui-resize-hover', 87958 /** 87959 * @private 87960 * @property CSS_PROXY 87961 * @description Class name given to the proxy element 87962 * @type String 87963 */ 87964 CSS_PROXY: 'yui-resize-proxy', 87965 /** 87966 * @private 87967 * @property CSS_WRAP 87968 * @description Class name given to the wrap element 87969 * @type String 87970 */ 87971 CSS_WRAP: 'yui-resize-wrap', 87972 /** 87973 * @private 87974 * @property CSS_KNOB 87975 * @description Class name used to make the knob style handles 87976 * @type String 87977 */ 87978 CSS_KNOB: 'yui-resize-knob', 87979 /** 87980 * @private 87981 * @property CSS_HIDDEN 87982 * @description Class name given to the wrap element to make all handles hidden 87983 * @type String 87984 */ 87985 CSS_HIDDEN: 'yui-resize-hidden', 87986 /** 87987 * @private 87988 * @property CSS_HANDLE 87989 * @description Class name given to all handles, used as a base for single handle names as well.. Handle "t" will get this.CSS_HANDLE + '-t' as well as this.CSS_HANDLE
87990 * @type String 87991 */ 87992 CSS_HANDLE: 'yui-resize-handle', 87993 /** 87994 * @private 87995 * @property CSS_STATUS 87996 * @description Class name given to the status element 87997 * @type String 87998 */ 87999 CSS_STATUS: 'yui-resize-status', 88000 /** 88001 * @private 88002 * @property CSS_GHOST 88003 * @description Class name given to the wrap element when the ghost property is active 88004 * @type String 88005 */ 88006 CSS_GHOST: 'yui-resize-ghost', 88007 /** 88008 * @private 88009 * @property CSS_RESIZING 88010 * @description Class name given to the wrap element when a resize action is taking place. 88011 * @type String 88012 */ 88013 CSS_RESIZING: 'yui-resize-resizing', 88014 /** 88015 * @private 88016 * @property _resizeEvent 88017 * @description The mouse event used to resize with 88018 * @type Event 88019 */ 88020 _resizeEvent: null, 88021 /** 88022 * @private 88023 * @property dd 88024 * @description The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance used if draggable is true 88025 * @type Object 88026 */ 88027 dd: null, 88028 /** 88029 * @private 88030 * @property browser 88031 * @description A copy of the YAHOO.env.ua property 88032 * @type Object 88033 */ 88034 browser: YAHOO.env.ua, 88035 /** 88036 * @private 88037 * @property _locked 88038 * @description A flag to show if the resize is locked 88039 * @type Boolean 88040 */ 88041 _locked: null, 88042 /** 88043 * @private 88044 * @property _positioned 88045 * @description A flag to show if the element is absolutely positioned 88046 * @type Boolean 88047 */ 88048 _positioned: null, 88049 /** 88050 * @private 88051 * @property _dds 88052 * @description An Object containing references to all of the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instances used for the resize handles 88053 * @type Object 88054 */ 88055 _dds: null, 88056 /** 88057 * @private 88058 * @property _wrap 88059 * @description The HTML reference of the element wrapper 88060 * @type HTMLElement 88061 */ 88062 _wrap: null, 88063 /** 88064 * @private 88065 * @property _proxy 88066 * @description The HTML reference of the element proxy 88067 * @type HTMLElement 88068 */ 88069 _proxy: null, 88070 /** 88071 * @private 88072 * @property _handles 88073 * @description An object containing references to all of the resize handles. 88074 * @type Object 88075 */ 88076 _handles: null, 88077 /** 88078 * @private 88079 * @property _currentHandle 88080 * @description The string identifier of the currently active handle. e.g. 'r', 'br', 'tl'
88081 * @type String 88082 */ 88083 _currentHandle: null, 88084 /** 88085 * @private 88086 * @property _currentDD 88087 * @description A link to the currently active DD object 88088 * @type Object 88089 */ 88090 _currentDD: null, 88091 /** 88092 * @private 88093 * @property _cache 88094 * @description An lookup table containing key information for the element being resized. e.g. height, width, x position, y position, etc.. 88095 * @type Object 88096 */ 88097 _cache: null, 88098 /** 88099 * @private 88100 * @property _active 88101 * @description Flag to show if the resize is active. Used for events. 88102 * @type Boolean 88103 */ 88104 _active: null, 88105 /** 88106 * @private 88107 * @method _createProxy 88108 * @description Creates the proxy element if the proxy config is true 88109 */ 88110 _createProxy: function() { 88111 if (this.get('proxy')) { 88112 YAHOO.log('Creating the Proxy Element', 'info', 'Resize'); 88113 this._proxy = document.createElement('div'); 88114 this._proxy.className = this.CSS_PROXY; 88115 this._proxy.style.height = this.get('element').clientHeight + 'px'; 88116 this._proxy.style.width = this.get('element').clientWidth + 'px'; 88117 this._wrap.parentNode.appendChild(this._proxy); 88118 } else { 88119 YAHOO.log('No proxy element, turn off animate config option', 'info', 'Resize'); 88120 this.set('animate', false); 88121 } 88122 }, 88123 /** 88124 * @private 88125 * @method _createWrap 88126 * @description Creates the wrap element if the wrap config is true. It will auto wrap the following element types: img, textarea, input, iframe, select 88127 */ 88128 _createWrap: function() { 88129 YAHOO.log('Create the wrap element', 'info', 'Resize'); 88130 this._positioned = false; 88131 //Force wrap for elements that can't have children 88132 if (this.get('wrap') === false) { 88133 switch (this.get('element').tagName.toLowerCase()) { 88134 case 'img': 88135 case 'textarea': 88136 case 'input': 88137 case 'iframe': 88138 case 'select': 88139 YAHOO.log('Auto-wrapping the element (' + this.get('element').tagName.toLowerCase() + ')', 'warn', 'Resize'); 88140 this.set('wrap', true); 88141 break; 88142 } 88143 } 88144 if (this.get('wrap') === true) { 88145 YAHOO.log('Creating the wrap element', 'info', 'Resize'); 88146 this._wrap = document.createElement('div'); 88147 this._wrap.id = this.get('element').id + '_wrap'; 88148 this._wrap.className = this.CSS_WRAP; 88149 if (this.get('element').tagName.toLowerCase() == 'textarea') { 88150 D.addClass(this._wrap, 'yui-resize-textarea'); 88151 } 88152 D.setStyle(this._wrap, 'width', this.get('width') + 'px'); 88153 D.setStyle(this._wrap, 'height', this.get('height') + 'px'); 88154 D.setStyle(this._wrap, 'z-index', this.getStyle('z-index')); 88155 this.setStyle('z-index', 0); 88156 var pos = D.getStyle(this.get('element'), 'position'); 88157 D.setStyle(this._wrap, 'position', ((pos == 'static') ? 'relative' : pos)); 88158 D.setStyle(this._wrap, 'top', D.getStyle(this.get('element'), 'top')); 88159 D.setStyle(this._wrap, 'left', D.getStyle(this.get('element'), 'left')); 88160 if (D.getStyle(this.get('element'), 'position') == 'absolute') { 88161 this._positioned = true; 88162 YAHOO.log('The element is positioned absolute', 'info', 'Resize'); 88163 D.setStyle(this.get('element'), 'position', 'relative'); 88164 D.setStyle(this.get('element'), 'top', '0'); 88165 D.setStyle(this.get('element'), 'left', '0'); 88166 } 88167 var par = this.get('element').parentNode; 88168 par.replaceChild(this._wrap, this.get('element')); 88169 this._wrap.appendChild(this.get('element')); 88170 } else { 88171 this._wrap = this.get('element'); 88172 if (D.getStyle(this._wrap, 'position') == 'absolute') { 88173 this._positioned = true; 88174 } 88175 } 88176 if (this.get('draggable')) { 88177 this._setupDragDrop(); 88178 } 88179 if (this.get('hover')) { 88180 D.addClass(this._wrap, this.CSS_HOVER); 88181 } 88182 if (this.get('knobHandles')) { 88183 D.addClass(this._wrap, this.CSS_KNOB); 88184 } 88185 if (this.get('hiddenHandles')) { 88186 D.addClass(this._wrap, this.CSS_HIDDEN); 88187 } 88188 D.addClass(this._wrap, this.CSS_RESIZE); 88189 }, 88190 /** 88191 * @private 88192 * @method _setupDragDrop
88193 * @description Setup the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance on the element 88194 */ 88195 _setupDragDrop: function() { 88196 YAHOO.log('Setting up the dragdrop instance on the element', 'info', 'Resize'); 88197 D.addClass(this._wrap, this.CSS_DRAG); 88198 this.dd = new YAHOO.util.DD(this._wrap, this.get('id') + '-resize', { dragOnly: true, useShim: this.get('useShim') }); 88199 this.dd.on('dragEvent', function() { 88200 this.fireEvent('dragEvent', arguments); 88201 }, this, true); 88202 }, 88203 /** 88204 * @private 88205 * @method _createHandles 88206 * @description Creates the handles as specified in the config 88207 */ 88208 _createHandles: function() { 88209 YAHOO.log('Creating the handles', 'info', 'Resize'); 88210 this._handles = {}; 88211 this._dds = {}; 88212 var h = this.get('handles'); 88213 for (var i = 0; i < h.length; i++) { 88214 YAHOO.log('Creating handle position: ' + h[i], 'info', 'Resize'); 88215 this._handles[h[i]] = document.createElement('div'); 88216 this._handles[h[i]].id = D.generateId(this._handles[h[i]]); 88217 this._handles[h[i]].className = this.CSS_HANDLE + ' ' + this.CSS_HANDLE + '-' + h[i]; 88218 var k = document.createElement('div'); 88219 k.className = this.CSS_HANDLE + '-inner-' + h[i]; 88220 this._handles[h[i]].appendChild(k); 88221 this._wrap.appendChild(this._handles[h[i]]); 88222 Event.on(this._handles[h[i]], 'mouseover', this._handleMouseOver, this, true); 88223 Event.on(this._handles[h[i]], 'mouseout', this._handleMouseOut, this, true); 88224 this._dds[h[i]] = new YAHOO.util.DragDrop(this._handles[h[i]], this.get('id') + '-handle-' + h, { useShim: this.get('useShim') }); 88225 this._dds[h[i]].setPadding(15, 15, 15, 15); 88226 this._dds[h[i]].on('startDragEvent', this._handleStartDrag, this._dds[h[i]], this); 88227 this._dds[h[i]].on('mouseDownEvent', this._handleMouseDown, this._dds[h[i]], this); 88228 } 88229 YAHOO.log('Creating the Status box', 'info', 'Resize'); 88230 this._status = document.createElement('span'); 88231 this._status.className = this.CSS_STATUS; 88232 document.body.insertBefore(this._status, document.body.firstChild); 88233 }, 88234 /** 88235 * @private 88236 * @method _ieSelectFix 88237 * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer 88238 */ 88239 _ieSelectFix: function() { 88240 return false; 88241 }, 88242 /** 88243 * @private 88244 * @property _ieSelectBack 88245 * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it. 88246 */ 88247 _ieSelectBack: null, 88248 /** 88249 * @private 88250 * @method _setAutoRatio 88251 * @param {Event} ev A mouse event. 88252 * @description This method checks to see if the "autoRatio" config is set. If it is, we will check to see if the "Shift Key" is pressed. If so, we will set the config ratio to true. 88253 */ 88254 _setAutoRatio: function(ev) { 88255 if (this.get('autoRatio')) { 88256 YAHOO.log('Setting up AutoRatio', 'info', 'Resize'); 88257 if (ev && ev.shiftKey) { 88258 //Shift Pressed 88259 YAHOO.log('Shift key presses, turning on ratio', 'info', 'Resize'); 88260 this.set('ratio', true); 88261 } else { 88262 YAHOO.log('Resetting ratio back to default', 'info', 'Resize'); 88263 this.set('ratio', this._configs.ratio._initialConfig.value); 88264 } 88265 } 88266 }, 88267 /** 88268 * @private 88269 * @method _handleMouseDown 88270 * @param {Event} ev A mouse event. 88271 * @description This method preps the autoRatio on MouseDown. 88272 */ 88273 _handleMouseDown: function(ev) { 88274 if (this._locked) { 88275 YAHOO.log('Resize Locked', 'info', 'Resize'); 88276 return false;
88277 } 88278 if (D.getStyle(this._wrap, 'position') == 'absolute') { 88279 this._positioned = true; 88280 } 88281 if (ev) { 88282 this._setAutoRatio(ev); 88283 } 88284 if (this.browser.ie) { 88285 this._ieSelectBack = document.body.onselectstart; 88286 document.body.onselectstart = this._ieSelectFix; 88287 } 88288 }, 88289 /** 88290 * @private 88291 * @method _handleMouseOver 88292 * @param {Event} ev A mouse event. 88293 * @description Adds CSS class names to the handles 88294 */ 88295 _handleMouseOver: function(ev) { 88296 if (this._locked) { 88297 YAHOO.log('Resize Locked', 'info', 'Resize'); 88298 return false; 88299 } 88300 D.removeClass(this._wrap, this.CSS_RESIZE); 88301 88302 if (this.get('hover')) { 88303 D.removeClass(this._wrap, this.CSS_HOVER); 88304 } 88305 var tar = Event.getTarget(ev); 88306 if (!D.hasClass(tar, this.CSS_HANDLE)) { 88307 tar = tar.parentNode; 88308 } 88309 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) { 88310 D.addClass(tar, this.CSS_HANDLE + '-active'); 88311 for (var i in this._handles) { 88312 if (Lang.hasOwnProperty(this._handles, i)) { 88313 if (this._handles[i] == tar) { 88314 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active'); 88315 break; 88316 } 88317 } 88318 } 88319 } 88320 88321 D.addClass(this._wrap, this.CSS_RESIZE); 88322 }, 88323 /** 88324 * @private 88325 * @method _handleMouseOut 88326 * @param {Event} ev A mouse event. 88327 * @description Removes CSS class names to the handles 88328 */ 88329 _handleMouseOut: function(ev) { 88330 D.removeClass(this._wrap, this.CSS_RESIZE); 88331 if (this.get('hover') && !this._active) { 88332 D.addClass(this._wrap, this.CSS_HOVER); 88333 } 88334 var tar = Event.getTarget(ev); 88335 if (!D.hasClass(tar, this.CSS_HANDLE)) { 88336 tar = tar.parentNode; 88337 } 88338 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) { 88339 D.removeClass(tar, this.CSS_HANDLE + '-active'); 88340 for (var i in this._handles) { 88341 if (Lang.hasOwnProperty(this._handles, i)) { 88342 if (this._handles[i] == tar) { 88343 D.removeClass(tar, this.CSS_HANDLE + '-' + i + '-active'); 88344 break; 88345 } 88346 } 88347 } 88348 } 88349 D.addClass(this._wrap, this.CSS_RESIZE); 88350 }, 88351 /** 88352 * @private 88353 * @method _handleStartDrag 88354 * @param {Object} args The args passed from the CustomEvent. 88355 * @param {Object} dd The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> object we are working with. 88356 * @description Resizes the proxy, sets up the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> handlers, updates the status div and preps the cache 88357 */ 88358 _handleStartDrag: function(args, dd) { 88359 YAHOO.log('startDrag', 'info', 'Resize'); 88360 var tar = dd.getDragEl(); 88361 if (D.hasClass(tar, this.CSS_HANDLE)) { 88362 if (D.getStyle(this._wrap, 'position') == 'absolute') { 88363 this._positioned = true; 88364 } 88365 this._active = true; 88366 this._currentDD = dd; 88367 if (this._proxy) { 88368 YAHOO.log('Activate proxy element', 'info', 'Resize'); 88369 this._proxy.style.visibility = 'visible'; 88370 this._proxy.style.zIndex = '1000'; 88371 this._proxy.style.height = this.get('element').clientHeight + 'px'; 88372 this._proxy.style.width = this.get('element').clientWidth + 'px'; 88373 } 88374 88375 for (var i in this._handles) { 88376 if (Lang.hasOwnProperty(this._handles, i)) { 88377 if (this._handles[i] == tar) { 88378 this._currentHandle = i; 88379 var handle = '_handle_for_' + i; 88380 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active'); 88381 dd.on('dragEvent', this[handle], this, true); 88382 dd.on('mouseUpEvent', this._handleMouseUp, this, true); 88383 YAHOO.log('Adding DragEvents to: ' + i, 'info', 'Resize'); 88384 break; 88385 } 88386 } 88387 } 88388 88389 88390 D.addClass(tar, this.CSS_HANDLE + '-active'); 88391 88392 if (this.get('proxy')) { 88393 YAHOO.log('Posiiton Proxy Element', 'info', 'Resize'); 88394 var xy = D.getXY(this.get('element')); 88395 D.setXY(this._proxy, xy); 88396 if (this.get('ghost')) { 88397 YAHOO.log('Add Ghost Class', 'info', 'Resize'); 88398 this.addClass(this.CSS_GHOST); 88399 } 88400 } 88401 D.addClass(this._wrap, this.CSS_RESIZING); 88402 this._setCache(); 88403 this._updateStatus(this._cache.height, this._cache.width, this._cache.top, this._cache.left); 88404 YAHOO.log('Firing startResize Event', 'info', 'Resize'); 88405 this.fireEvent('startResize', { type: 'startresize', target: this}); 88406 } 88407 }, 88408 /** 88409 * @private 88410 * @method _setCache
88411 * @description Sets up the this._cache hash table. 88412 */ 88413 _setCache: function() { 88414 YAHOO.log('Setting up property cache', 'info', 'Resize'); 88415 this._cache.xy = D.getXY(this._wrap); 88416 D.setXY(this._wrap, this._cache.xy); 88417 this._cache.height = this.get('clientHeight'); 88418 this._cache.width = this.get('clientWidth'); 88419 this._cache.start.height = this._cache.height; 88420 this._cache.start.width = this._cache.width; 88421 this._cache.start.top = this._cache.xy[1]; 88422 this._cache.start.left = this._cache.xy[0]; 88423 this._cache.top = this._cache.xy[1]; 88424 this._cache.left = this._cache.xy[0]; 88425 this.set('height', this._cache.height, true); 88426 this.set('width', this._cache.width, true); 88427 }, 88428 /** 88429 * @private 88430 * @method _handleMouseUp 88431 * @param {Event} ev A mouse event. 88432 * @description Cleans up listeners, hides proxy element and removes class names. 88433 */ 88434 _handleMouseUp: function(ev) { 88435 this._active = false; 88436 88437 var handle = '_handle_for_' + this._currentHandle; 88438 this._currentDD.unsubscribe('dragEvent', this[handle], this, true); 88439 this._currentDD.unsubscribe('mouseUpEvent', this._handleMouseUp, this, true); 88440 88441 if (this._proxy) { 88442 YAHOO.log('Hide Proxy Element', 'info', 'Resize'); 88443 this._proxy.style.visibility = 'hidden'; 88444 this._proxy.style.zIndex = '-1'; 88445 if (this.get('setSize')) { 88446 YAHOO.log('Setting Size', 'info', 'Resize'); 88447 this.resize(ev, this._cache.height, this._cache.width, this._cache.top, this._cache.left, true); 88448 } else { 88449 YAHOO.log('Firing Resize Event', 'info', 'Resize'); 88450 this.fireEvent('resize', { ev: 'resize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left }); 88451 } 88452 88453 if (this.get('ghost')) { 88454 YAHOO.log('Removing Ghost Class', 'info', 'Resize'); 88455 this.removeClass(this.CSS_GHOST); 88456 } 88457 } 88458 88459 if (this.get('hover')) { 88460 D.addClass(this._wrap, this.CSS_HOVER); 88461 } 88462 if (this._status) { 88463 D.setStyle(this._status, 'display', 'none'); 88464 } 88465 if (this.browser.ie) { 88466 YAHOO.log('Resetting IE onselectstart function', 'info', 'Resize'); 88467 document.body.onselectstart = this._ieSelectBack; 88468 } 88469 88470 if (this.browser.ie) { 88471 D.removeClass(this._wrap, this.CSS_RESIZE); 88472 } 88473 88474 for (var i in this._handles) { 88475 if (Lang.hasOwnProperty(this._handles, i)) { 88476 D.removeClass(this._handles[i], this.CSS_HANDLE + '-active'); 88477 } 88478 } 88479 if (this.get('hover') && !this._active) { 88480 D.addClass(this._wrap, this.CSS_HOVER); 88481 } 88482 D.removeClass(this._wrap, this.CSS_RESIZING); 88483 88484 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-' + this._currentHandle + '-active'); 88485 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-active'); 88486 88487 if (this.browser.ie) { 88488 D.addClass(this._wrap, this.CSS_RESIZE); 88489 } 88490 88491 this._resizeEvent = null; 88492 this._currentHandle = null; 88493 88494 if (!this.get('animate')) { 88495 this.set('height', this._cache.height, true); 88496 this.set('width', this._cache.width, true); 88497 } 88498 88499 YAHOO.log('Firing endResize Event', 'info', 'Resize'); 88500 this.fireEvent('endResize', { ev: 'endResize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left }); 88501 }, 88502 /** 88503 * @private 88504 * @method _setRatio 88505 * @param {Number} h The height offset. 88506 * @param {Number} w The with offset. 88507 * @param {Number} t The top offset. 88508 * @param {Number} l The left offset.
88509 * @description Using the Height, Width, Top & Left, it recalcuates them based on the original element size. 88510 * @return {Array} The new Height, Width, Top & Left settings 88511 */ 88512 _setRatio: function(h, w, t, l) { 88513 YAHOO.log('Setting Ratio', 'info', 'Resize'); 88514 var oh = h, ow = w; 88515 if (this.get('ratio')) { 88516 var orgH = this._cache.height, 88517 orgW = this._cache.width, 88518 nh = parseInt(this.get('height'), 10), 88519 nw = parseInt(this.get('width'), 10), 88520 maxH = this.get('maxHeight'), 88521 minH = this.get('minHeight'), 88522 maxW = this.get('maxWidth'), 88523 minW = this.get('minWidth'); 88524 88525 switch (this._currentHandle) { 88526 case 'l': 88527 h = nh * (w / nw); 88528 h = Math.min(Math.max(minH, h), maxH); 88529 w = nw * (h / nh); 88530 t = (this._cache.start.top - (-((nh - h) / 2))); 88531 l = (this._cache.start.left - (-((nw - w)))); 88532 break; 88533 case 'r': 88534 h = nh * (w / nw); 88535 h = Math.min(Math.max(minH, h), maxH); 88536 w = nw * (h / nh); 88537 t = (this._cache.start.top - (-((nh - h) / 2))); 88538 break; 88539 case 't': 88540 w = nw * (h / nh); 88541 h = nh * (w / nw); 88542 l = (this._cache.start.left - (-((nw - w) / 2))); 88543 t = (this._cache.start.top - (-((nh - h)))); 88544 break; 88545 case 'b': 88546 w = nw * (h / nh); 88547 h = nh * (w / nw); 88548 l = (this._cache.start.left - (-((nw - w) / 2))); 88549 break; 88550 case 'bl': 88551 h = nh * (w / nw); 88552 w = nw * (h / nh); 88553 l = (this._cache.start.left - (-((nw - w)))); 88554 break; 88555 case 'br': 88556 h = nh * (w / nw); 88557 w = nw * (h / nh); 88558 break; 88559 case 'tl': 88560 h = nh * (w / nw); 88561 w = nw * (h / nh); 88562 l = (this._cache.start.left - (-((nw - w)))); 88563 t = (this._cache.start.top - (-((nh - h)))); 88564 break; 88565 case 'tr': 88566 h = nh * (w / nw); 88567 w = nw * (h / nh); 88568 l = (this._cache.start.left); 88569 t = (this._cache.start.top - (-((nh - h)))); 88570 break; 88571 } 88572 oh = this._checkHeight(h); 88573 ow = this._checkWidth(w); 88574 if ((oh != h) || (ow != w)) { 88575 t = 0; 88576 l = 0; 88577 if (oh != h) { 88578 ow = this._cache.width; 88579 } 88580 if (ow != w) { 88581 oh = this._cache.height; 88582 } 88583 } 88584 } 88585 return [oh, ow, t, l]; 88586 }, 88587 /** 88588 * @private 88589 * @method _updateStatus 88590 * @param {Number} h The new height setting. 88591 * @param {Number} w The new width setting. 88592 * @param {Number} t The new top setting. 88593 * @param {Number} l The new left setting. 88594 * @description Using the Height, Width, Top & Left, it updates the status element with the elements sizes. 88595 */ 88596 _updateStatus: function(h, w, t, l) { 88597 if (this._resizeEvent && (!Lang.isString(this._resizeEvent))) { 88598 YAHOO.log('Updating Status Box', 'info', 'Resize'); 88599 h = ((h === 0) ? this._cache.start.height : h); 88600 w = ((w === 0) ? this._cache.start.width : w); 88601 var h1 = parseInt(this.get('height'), 10), 88602 w1 = parseInt(this.get('width'), 10); 88603 88604 if (isNaN(h1)) { 88605 h1 = parseInt(h, 10); 88606 } 88607 if (isNaN(w1)) { 88608 w1 = parseInt(w, 10); 88609 } 88610 var diffH = (parseInt(h, 10) - h1); 88611 var diffW = (parseInt(w, 10) - w1); 88612 this._cache.offsetHeight = diffH; 88613 this._cache.offsetWidth = diffW; 88614 if (this.get('status')) { 88615 YAHOO.log('Showing Status Box', 'info', 'Resize'); 88616 D.setStyle(this._status, 'display', 'inline'); 88617 //This will cause IE8 to crash if the status box is hidden.. 88618 this._status.innerHTML = '<strong>' + parseInt(h, 10) + ' x ' + parseInt(w, 10) + '</strong><em>' + ((diffH > 0) ? '+' : '') + diffH + ' x ' + ((diffW > 0) ? '+' : '') + diffW + '</em>'; 88619 D.setXY(this._status, [Event.getPageX(this._resizeEvent) + 12, Event.getPageY(this._resizeEvent) + 12]); 88620 } 88621 } 88622 }, 88623 /** 88624 * @method lock
88625 * @description Lock the resize so it can't be resized 88626 * @param {Boolean} dd If the draggable config is set, lock it too 88627 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance 88628 */ 88629 lock: function(dd) { 88630 this._locked = true; 88631 if (dd && this.dd) { 88632 D.removeClass(this._wrap, 'yui-draggable'); 88633 this.dd.lock(); 88634 } 88635 return this; 88636 }, 88637 /** 88638 * @method unlock 88639 * @description Unlock the resize so it can be resized 88640 * @param {Boolean} dd If the draggable config is set, unlock it too 88641 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance 88642 */ 88643 unlock: function(dd) { 88644 this._locked = false; 88645 if (dd && this.dd) { 88646 D.addClass(this._wrap, 'yui-draggable'); 88647 this.dd.unlock(); 88648 } 88649 return this; 88650 }, 88651 /** 88652 * @method isLocked 88653 * @description Check the locked status of the resize instance 88654 * @return {Boolean} 88655 */ 88656 isLocked: function() { 88657 return this._locked; 88658 }, 88659 /** 88660 * @method reset 88661 * @description Resets the element to is start state. 88662 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance 88663 */ 88664 reset: function() { 88665 YAHOO.log('Resetting to cached sizes and position', 'info', 'Resize'); 88666 this.resize(null, this._cache.start.height, this._cache.start.width, this._cache.start.top, this._cache.start.left, true); 88667 return this; 88668 }, 88669 /** 88670 * @private 88671 * @method resize 88672 * @param {Event} ev The mouse event. 88673 * @param {Number} h The new height setting. 88674 * @param {Number} w The new width setting. 88675 * @param {Number} t The new top setting. 88676 * @param {Number} l The new left setting. 88677 * @param {Boolean} force Resize the element (used for proxy resize). 88678 * @param {Boolean} silent Don't fire the beforeResize Event. 88679 * @description Resizes the element, wrapper or proxy based on the data from the handlers. 88680 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance 88681 */ 88682 resize: function(ev, h, w, t, l, force, silent) { 88683 if (this._locked) { 88684 YAHOO.log('Resize Locked', 'info', 'Resize'); 88685 return false; 88686 } 88687 YAHOO.log('Resize: ' + h + ',' + w + ',' + t + ',' + l, 'info', 'Resize'); 88688 this._resizeEvent = ev; 88689 var el = this._wrap, anim = this.get('animate'), set = true; 88690 if (this._proxy && !force) { 88691 el = this._proxy; 88692 anim = false; 88693 } 88694 this._setAutoRatio(ev); 88695 if (this._positioned) { 88696 if (this._proxy) { 88697 t = this._cache.top - t; 88698 l = this._cache.left - l; 88699 } 88700 } 88701 88702 88703 var ratio = this._setRatio(h, w, t, l); 88704 h = parseInt(ratio[0], 10); 88705 w = parseInt(ratio[1], 10); 88706 t = parseInt(ratio[2], 10); 88707 l = parseInt(ratio[3], 10); 88708 88709 if (t == 0) { 88710 //No Offset, get from cache 88711 t = D.getY(el); 88712 } 88713 if (l == 0) { 88714 //No Offset, get from cache 88715 l = D.getX(el); 88716 } 88717 88718 88719 88720 if (this._positioned) { 88721 if (this._proxy && force) { 88722 if (!anim) { 88723 el.style.top = this._proxy.style.top; 88724 el.style.left = this._proxy.style.left; 88725 } else { 88726 t = this._proxy.style.top; 88727 l = this._proxy.style.left; 88728 } 88729 } else { 88730 if (!this.get('ratio') && !this._proxy) { 88731 t = this._cache.top + -(t); 88732 l = this._cache.left + -(l); 88733 } 88734 if (t) { 88735 if (this.get('minY')) { 88736 if (t < this.get('minY')) { 88737 t = this.get('minY'); 88738 } 88739 } 88740 if (this.get('maxY')) { 88741 if (t > this.get('maxY')) { 88742 t = this.get('maxY'); 88743 } 88744 } 88745 } 88746 if (l) { 88747 if (this.get('minX')) { 88748 if (l < this.get('minX')) { 88749 l = this.get('minX'); 88750 } 88751 } 88752 if (this.get('maxX')) { 88753 if ((l + w) > this.get('maxX')) { 88754 l = (this.get('maxX') - w); 88755 } 88756 } 88757 } 88758 } 88759 } 88760 if (!silent) { 88761 YAHOO.log('beforeResize', 'info', 'Resize'); 88762 var beforeReturn = this.fireEvent('beforeResize', { ev: 'beforeResize', target: this, height: h, width: w, top: t, left: l }); 88763 if (beforeReturn === false) { 88764 YAHOO.log('Resized cancelled because befireResize returned false', 'info', 'Resize'); 88765 return false;
88766 } 88767 } 88768 88769 this._updateStatus(h, w, t, l); 88770 88771 88772 if (this._positioned) { 88773 if (this._proxy && force) { 88774 //Do nothing 88775 } else { 88776 if (t) { 88777 D.setY(el, t); 88778 this._cache.top = t; 88779 } 88780 if (l) { 88781 D.setX(el, l); 88782 this._cache.left = l; 88783 } 88784 } 88785 } 88786 if (h) { 88787 if (!anim) { 88788 set = true; 88789 if (this._proxy && force) { 88790 if (!this.get('setSize')) { 88791 set = false; 88792 } 88793 } 88794 if (set) { 88795 el.style.height = h + 'px'; 88796 } 88797 if ((this._proxy && force) || !this._proxy) { 88798 if (this._wrap != this.get('element')) { 88799 this.get('element').style.height = h + 'px'; 88800 } 88801 } 88802 } 88803 this._cache.height = h; 88804 } 88805 if (w) { 88806 this._cache.width = w; 88807 if (!anim) { 88808 set = true; 88809 if (this._proxy && force) { 88810 if (!this.get('setSize')) { 88811 set = false; 88812 } 88813 } 88814 if (set) { 88815 el.style.width = w + 'px'; 88816 } 88817 if ((this._proxy && force) || !this._proxy) { 88818 if (this._wrap != this.get('element')) { 88819 this.get('element').style.width = w + 'px'; 88820 } 88821 } 88822 } 88823 } 88824 if (anim) { 88825 if (YAHOO.util.Anim) { 88826 var _anim = new YAHOO.util.Anim(el, { 88827 height: { 88828 to: this._cache.height 88829 }, 88830 width: { 88831 to: this._cache.width 88832 } 88833 }, this.get('animateDuration'), this.get('animateEasing')); 88834 if (this._positioned) { 88835 if (t) { 88836 _anim.attributes.top = { 88837 to: parseInt(t, 10) 88838 }; 88839 } 88840 if (l) { 88841 _anim.attributes.left = { 88842 to: parseInt(l, 10) 88843 }; 88844 } 88845 } 88846 88847 if (this._wrap != this.get('element')) { 88848 _anim.onTween.subscribe(function() { 88849 this.get('element').style.height = el.style.height; 88850 this.get('element').style.width = el.style.width; 88851 }, this, true); 88852 } 88853 88854 _anim.onComplete.subscribe(function() { 88855 YAHOO.log('Animation onComplete fired', 'info', 'Resize'); 88856 this.set('height', h); 88857 this.set('width', w); 88858 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l }); 88859 }, this, true); 88860 _anim.animate(); 88861 88862 } 88863 } else { 88864 if (this._proxy && !force) { 88865 YAHOO.log('proxyResize', 'info', 'Resize'); 88866 this.fireEvent('proxyResize', { ev: 'proxyresize', target: this, height: h, width: w, top: t, left: l }); 88867 } else { 88868 YAHOO.log('resize', 'info', 'Resize'); 88869 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l }); 88870 } 88871 } 88872 return this; 88873 }, 88874 /** 88875 * @private 88876 * @method _handle_for_br 88877 * @param {Object} args The arguments from the CustomEvent.
88878 * @description Handles the sizes for the Bottom Right handle. 88879 */ 88880 _handle_for_br: function(args) { 88881 YAHOO.log('Handle BR', 'info', 'Resize'); 88882 var newW = this._setWidth(args.e); 88883 var newH = this._setHeight(args.e); 88884 this.resize(args.e, newH, newW, 0, 0); 88885 }, 88886 /** 88887 * @private 88888 * @method _handle_for_bl 88889 * @param {Object} args The arguments from the CustomEvent. 88890 * @description Handles the sizes for the Bottom Left handle. 88891 */ 88892 _handle_for_bl: function(args) { 88893 YAHOO.log('Handle BL', 'info', 'Resize'); 88894 var newW = this._setWidth(args.e, true); 88895 var newH = this._setHeight(args.e); 88896 var l = (newW - this._cache.width); 88897 this.resize(args.e, newH, newW, 0, l); 88898 }, 88899 /** 88900 * @private 88901 * @method _handle_for_tl 88902 * @param {Object} args The arguments from the CustomEvent. 88903 * @description Handles the sizes for the Top Left handle. 88904 */ 88905 _handle_for_tl: function(args) { 88906 YAHOO.log('Handle TL', 'info', 'Resize'); 88907 var newW = this._setWidth(args.e, true); 88908 var newH = this._setHeight(args.e, true); 88909 var t = (newH - this._cache.height); 88910 var l = (newW - this._cache.width); 88911 this.resize(args.e, newH, newW, t, l); 88912 }, 88913 /** 88914 * @private 88915 * @method _handle_for_tr 88916 * @param {Object} args The arguments from the CustomEvent. 88917 * @description Handles the sizes for the Top Right handle. 88918 */ 88919 _handle_for_tr: function(args) { 88920 YAHOO.log('Handle TR', 'info', 'Resize'); 88921 var newW = this._setWidth(args.e); 88922 var newH = this._setHeight(args.e, true); 88923 var t = (newH - this._cache.height); 88924 this.resize(args.e, newH, newW, t, 0); 88925 }, 88926 /** 88927 * @private 88928 * @method _handle_for_r 88929 * @param {Object} args The arguments from the CustomEvent. 88930 * @description Handles the sizes for the Right handle. 88931 */ 88932 _handle_for_r: function(args) { 88933 YAHOO.log('Handle R', 'info', 'Resize');
88934 this._dds.r.setYConstraint(0,0); 88935 var newW = this._setWidth(args.e); 88936 this.resize(args.e, 0, newW, 0, 0); 88937 }, 88938 /** 88939 * @private 88940 * @method _handle_for_l 88941 * @param {Object} args The arguments from the CustomEvent. 88942 * @description Handles the sizes for the Left handle. 88943 */ 88944 _handle_for_l: function(args) { 88945 YAHOO.log('Handle L', 'info', 'Resize'); 88946 this._dds.l.setYConstraint(0,0); 88947 var newW = this._setWidth(args.e, true); 88948 var l = (newW - this._cache.width); 88949 this.resize(args.e, 0, newW, 0, l); 88950 }, 88951 /** 88952 * @private 88953 * @method _handle_for_b 88954 * @param {Object} args The arguments from the CustomEvent. 88955 * @description Handles the sizes for the Bottom handle. 88956 */ 88957 _handle_for_b: function(args) { 88958 YAHOO.log('Handle B', 'info', 'Resize'); 88959 this._dds.b.setXConstraint(0,0); 88960 var newH = this._setHeight(args.e); 88961 this.resize(args.e, newH, 0, 0, 0); 88962 }, 88963 /** 88964 * @private 88965 * @method _handle_for_t 88966 * @param {Object} args The arguments from the CustomEvent. 88967 * @description Handles the sizes for the Top handle. 88968 */ 88969 _handle_for_t: function(args) { 88970 YAHOO.log('Handle T', 'info', 'Resize'); 88971 this._dds.t.setXConstraint(0,0); 88972 var newH = this._setHeight(args.e, true); 88973 var t = (newH - this._cache.height); 88974 this.resize(args.e, newH, 0, t, 0); 88975 }, 88976 /** 88977 * @private 88978 * @method _setWidth 88979 * @param {Event} ev The mouse event. 88980 * @param {Boolean} flip Argument to determine the direction of the movement. 88981 * @description Calculates the width based on the mouse event. 88982 * @return {Number} The new value 88983 */ 88984 _setWidth: function(ev, flip) { 88985 YAHOO.log('Set width based on Event', 'info', 'Resize'); 88986 var xy = this._cache.xy[0], 88987 w = this._cache.width, 88988 x = Event.getPageX(ev), 88989 nw = (x - xy); 88990 88991 if (flip) { 88992 nw = (xy - x) + parseInt(this.get('width'), 10); 88993 } 88994 88995 nw = this._snapTick(nw, this.get('xTicks')); 88996 nw = this._checkWidth(nw); 88997 return nw; 88998 }, 88999 /** 89000 * @private 89001 * @method _checkWidth 89002 * @param {Number} w The width to check. 89003 * @description Checks the value passed against the maxWidth and minWidth. 89004 * @return {Number} the new value 89005 */ 89006 _checkWidth: function(w) { 89007 YAHOO.log('Checking the min/max width', 'info', 'Resize'); 89008 if (this.get('minWidth')) { 89009 if (w <= this.get('minWidth')) { 89010 YAHOO.log('Using minWidth', 'info', 'Resize'); 89011 w = this.get('minWidth'); 89012 } 89013 } 89014 if (this.get('maxWidth')) { 89015 if (w >= this.get('maxWidth')) { 89016 YAHOO.log('Using Max Width', 'info', 'Resize'); 89017 w = this.get('maxWidth'); 89018 } 89019 } 89020 return w; 89021 }, 89022 /** 89023 * @private 89024 * @method _checkHeight 89025 * @param {Number} h The height to check. 89026 * @description Checks the value passed against the maxHeight and minHeight. 89027 * @return {Number} The new value 89028 */ 89029 _checkHeight: function(h) { 89030 YAHOO.log('Checking the min/max height', 'info', 'Resize'); 89031 if (this.get('minHeight')) { 89032 if (h <= this.get('minHeight')) { 89033 YAHOO.log('Using minHeight', 'info', 'Resize'); 89034 h = this.get('minHeight'); 89035 } 89036 } 89037 if (this.get('maxHeight')) { 89038 if (h >= this.get('maxHeight')) { 89039 YAHOO.log('using maxHeight', 'info', 'Resize'); 89040 h = this.get('maxHeight'); 89041 } 89042 } 89043 return h; 89044 }, 89045 /** 89046 * @private 89047 * @method _setHeight 89048 * @param {Event} ev The mouse event. 89049 * @param {Boolean} flip Argument to determine the direction of the movement.
89050 * @description Calculated the height based on the mouse event. 89051 * @return {Number} The new value 89052 */ 89053 _setHeight: function(ev, flip) { 89054 YAHOO.log('Setting the height based on the Event', 'info', 'Resize'); 89055 var xy = this._cache.xy[1], 89056 h = this._cache.height, 89057 y = Event.getPageY(ev), 89058 nh = (y - xy); 89059 89060 if (flip) { 89061 nh = (xy - y) + parseInt(this.get('height'), 10); 89062 } 89063 nh = this._snapTick(nh, this.get('yTicks')); 89064 nh = this._checkHeight(nh); 89065 89066 return nh; 89067 }, 89068 /** 89069 * @private 89070 * @method _snapTick 89071 * @param {Number} size The size to tick against. 89072 * @param {Number} pix The tick pixels. 89073 * @description Adjusts the number based on the ticks used. 89074 * @return {Number} the new snapped position 89075 */ 89076 _snapTick: function(size, pix) { 89077 YAHOO.log('Snapping to ticks', 'info', 'Resize'); 89078 if (!size || !pix) { 89079 return size; 89080 } 89081 var _s = size; 89082 var _x = size % pix; 89083 if (_x > 0) { 89084 if (_x > (pix / 2)) { 89085 _s = size + (pix - _x); 89086 } else { 89087 _s = size - _x; 89088 } 89089 } 89090 return _s; 89091 }, 89092 /** 89093 * @private 89094 * @method init 89095 * @description The Resize class's initialization method 89096 */ 89097 init: function(p_oElement, p_oAttributes) { 89098 YAHOO.log('init', 'info', 'Resize'); 89099 this._locked = false; 89100 this._cache = { 89101 xy: [], 89102 height: 0, 89103 width: 0, 89104 top: 0, 89105 left: 0, 89106 offsetHeight: 0, 89107 offsetWidth: 0, 89108 start: { 89109 height: 0, 89110 width: 0, 89111 top: 0, 89112 left: 0 89113 } 89114 }; 89115 89116 Resize.superclass.init.call(this, p_oElement, p_oAttributes); 89117 89118 this.set('setSize', this.get('setSize')); 89119 89120 if (p_oAttributes.height) { 89121 this.set('height', parseInt(p_oAttributes.height, 10)); 89122 } else { 89123 var h = this.getStyle('height'); 89124 if (h == 'auto') { 89125 this.set('height', parseInt(this.get('element').offsetHeight, 10)); 89126 } 89127 } 89128 if (p_oAttributes.width) { 89129 this.set('width', parseInt(p_oAttributes.width, 10)); 89130 } else { 89131 var w = this.getStyle('width'); 89132 if (w == 'auto') { 89133 this.set('width', parseInt(this.get('element').offsetWidth, 10)); 89134 } 89135 } 89136 89137 var id = p_oElement; 89138 if (!Lang.isString(id)) { 89139 id = D.generateId(id); 89140 } 89141 Resize._instances[id] = this; 89142 89143 this._active = false; 89144 89145 this._createWrap(); 89146 this._createProxy(); 89147 this._createHandles(); 89148 89149 }, 89150 /** 89151 * @method getProxyEl 89152 * @description Get the HTML reference for the proxy, returns null if no proxy. 89153 * @return {HTMLElement} The proxy element 89154 */ 89155 getProxyEl: function() { 89156 return this._proxy; 89157 }, 89158 /** 89159 * @method getWrapEl 89160 * @description Get the HTML reference for the wrap element, returns the current element if not wrapped. 89161 * @return {HTMLElement} The wrap element 89162 */ 89163 getWrapEl: function() { 89164 return this._wrap; 89165 }, 89166 /** 89167 * @method getStatusEl 89168 * @description Get the HTML reference for the status element. 89169 * @return {HTMLElement} The status element 89170 */ 89171 getStatusEl: function() { 89172 return this._status; 89173 }, 89174 /** 89175 * @method getActiveHandleEl
89176 * @description Get the HTML reference for the currently active resize handle. 89177 * @return {HTMLElement} The handle element that is active 89178 */ 89179 getActiveHandleEl: function() { 89180 return this._handles[this._currentHandle]; 89181 }, 89182 /** 89183 * @method isActive 89184 * @description Returns true or false if a resize operation is currently active on the element. 89185 * @return {Boolean} 89186 */ 89187 isActive: function() { 89188 return ((this._active) ? true : false); 89189 }, 89190 /** 89191 * @private 89192 * @method initAttributes 89193 * @description Initializes all of the configuration attributes used to create a resizable element. 89194 * @param {Object} attr Object literal specifying a set of 89195 * configuration attributes used to create the utility. 89196 */ 89197 initAttributes: function(attr) { 89198 Resize.superclass.initAttributes.call(this, attr); 89199 89200 /** 89201 * @attribute useShim 89202 * @description This setting will be passed to the DragDrop instances on the resize handles and for the draggable property. 89203 * This property should be used if you want the resize handles to work over iframe and other elements. 89204 * @type Boolean 89205 */ 89206 this.setAttributeConfig('useShim', { 89207 value: ((attr.useShim === true) ? true : false), 89208 validator: YAHOO.lang.isBoolean, 89209 method: function(u) { 89210 for (var i in this._dds) { 89211 if (Lang.hasOwnProperty(this._dds, i)) { 89212 this._dds[i].useShim = u; 89213 } 89214 } 89215 if (this.dd) { 89216 this.dd.useShim = u; 89217 } 89218 } 89219 }); 89220 /** 89221 * @attribute setSize 89222 * @description Set the size of the resized element, if set to false the element will not be auto resized, 89223 * the resize event will contain the dimensions so the end user can resize it on their own. 89224 * This setting will only work with proxy set to true and animate set to false. 89225 * @type Boolean 89226 */ 89227 this.setAttributeConfig('setSize', { 89228 value: ((attr.setSize === false) ? false : true), 89229 validator: YAHOO.lang.isBoolean 89230 }); 89231 89232 /** 89233 * @attribute wrap 89234 * @description Should we wrap the element 89235 * @type Boolean 89236 */ 89237 this.setAttributeConfig('wrap', { 89238 writeOnce: true, 89239 validator: YAHOO.lang.isBoolean, 89240 value: attr.wrap || false 89241 }); 89242 89243 /** 89244 * @attribute handles 89245 * @description The handles to use (any combination of): 't', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'. Defaults to: ['r', 'b', 'br']. 89246 * Can use a shortcut of All. Note: 8 way resizing should be done on an element that is absolutely positioned. 89247 * @type Array 89248 */ 89249 this.setAttributeConfig('handles', { 89250 writeOnce: true, 89251 value: attr.handles || ['r', 'b', 'br'], 89252 validator: function(handles) { 89253 if (Lang.isString(handles) && handles.toLowerCase() == 'all') { 89254 handles = ['t', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr']; 89255 } 89256 if (!Lang.isArray(handles)) { 89257 handles = handles.replace(/, /g, ','); 89258 handles = handles.split(','); 89259 } 89260 this._configs.handles.value = handles; 89261 } 89262 }); 89263 89264 /** 89265 * @attribute width 89266 * @description The width of the element 89267 * @type Number 89268 */ 89269 this.setAttributeConfig('width', { 89270 value: attr.width || parseInt(this.getStyle('width'), 10), 89271 validator: YAHOO.lang.isNumber, 89272 method: function(width) { 89273 width = parseInt(width, 10); 89274 if (width > 0) { 89275 if (this.get('setSize')) { 89276 this.setStyle('width', width + 'px'); 89277 } 89278 this._cache.width = width; 89279 this._configs.width.value = width; 89280 } 89281 } 89282 }); 89283 89284 /** 89285 * @attribute height
89286 * @description The height of the element 89287 * @type Number 89288 */ 89289 this.setAttributeConfig('height', { 89290 value: attr.height || parseInt(this.getStyle('height'), 10), 89291 validator: YAHOO.lang.isNumber, 89292 method: function(height) { 89293 height = parseInt(height, 10); 89294 if (height > 0) { 89295 if (this.get('setSize')) { 89296 this.setStyle('height', height + 'px'); 89297 } 89298 this._cache.height = height; 89299 this._configs.height.value = height; 89300 } 89301 } 89302 }); 89303 89304 /** 89305 * @attribute minWidth 89306 * @description The minimum width of the element 89307 * @type Number 89308 */ 89309 this.setAttributeConfig('minWidth', { 89310 value: attr.minWidth || 15, 89311 validator: YAHOO.lang.isNumber 89312 }); 89313 89314 /** 89315 * @attribute minHeight 89316 * @description The minimum height of the element 89317 * @type Number 89318 */ 89319 this.setAttributeConfig('minHeight', { 89320 value: attr.minHeight || 15, 89321 validator: YAHOO.lang.isNumber 89322 }); 89323 89324 /** 89325 * @attribute maxWidth 89326 * @description The maximum width of the element 89327 * @type Number 89328 */ 89329 this.setAttributeConfig('maxWidth', { 89330 value: attr.maxWidth || 10000, 89331 validator: YAHOO.lang.isNumber 89332 }); 89333 89334 /** 89335 * @attribute maxHeight 89336 * @description The maximum height of the element 89337 * @type Number 89338 */ 89339 this.setAttributeConfig('maxHeight', { 89340 value: attr.maxHeight || 10000, 89341 validator: YAHOO.lang.isNumber 89342 }); 89343 89344 /** 89345 * @attribute minY 89346 * @description The minimum y coord of the element 89347 * @type Number 89348 */ 89349 this.setAttributeConfig('minY', { 89350 value: attr.minY || false 89351 }); 89352 89353 /** 89354 * @attribute minX 89355 * @description The minimum x coord of the element 89356 * @type Number 89357 */ 89358 this.setAttributeConfig('minX', { 89359 value: attr.minX || false 89360 }); 89361 /** 89362 * @attribute maxY 89363 * @description The max y coord of the element 89364 * @type Number 89365 */ 89366 this.setAttributeConfig('maxY', { 89367 value: attr.maxY || false 89368 }); 89369 89370 /** 89371 * @attribute maxX 89372 * @description The max x coord of the element 89373 * @type Number 89374 */ 89375 this.setAttributeConfig('maxX', { 89376 value: attr.maxX || false 89377 }); 89378 89379 /** 89380 * @attribute animate 89381 * @description Should be use animation to resize the element (can only be used if we use proxy). 89382 * @type Boolean 89383 */ 89384 this.setAttributeConfig('animate', { 89385 value: attr.animate || false, 89386 validator: function(value) { 89387 var ret = true; 89388 if (!YAHOO.util.Anim) { 89389 ret = false;
89390 } 89391 return ret; 89392 } 89393 }); 89394 89395 /** 89396 * @attribute animateEasing 89397 * @description The Easing to apply to the animation. 89398 * @type Object 89399 */ 89400 this.setAttributeConfig('animateEasing', { 89401 value: attr.animateEasing || function() { 89402 var easing = false; 89403 if (YAHOO.util.Easing && YAHOO.util.Easing.easeOut) { 89404 easing = YAHOO.util.Easing.easeOut; 89405 } 89406 return easing; 89407 }() 89408 }); 89409 89410 /** 89411 * @attribute animateDuration 89412 * @description The Duration to apply to the animation. 89413 * @type Number 89414 */ 89415 this.setAttributeConfig('animateDuration', { 89416 value: attr.animateDuration || 0.5 89417 }); 89418 89419 /** 89420 * @attribute proxy 89421 * @description Resize a proxy element instead of the real element. 89422 * @type Boolean 89423 */ 89424 this.setAttributeConfig('proxy', { 89425 value: attr.proxy || false, 89426 validator: YAHOO.lang.isBoolean 89427 }); 89428 89429 /** 89430 * @attribute ratio 89431 * @description Maintain the element's ratio when resizing. 89432 * @type Boolean 89433 */ 89434 this.setAttributeConfig('ratio', { 89435 value: attr.ratio || false, 89436 validator: YAHOO.lang.isBoolean 89437 }); 89438 89439 /** 89440 * @attribute ghost 89441 * @description Apply an opacity filter to the element being resized (only works with proxy). 89442 * @type Boolean 89443 */ 89444 this.setAttributeConfig('ghost', { 89445 value: attr.ghost || false, 89446 validator: YAHOO.lang.isBoolean 89447 }); 89448 89449 /** 89450 * @attribute draggable 89451 * @description A convienence method to make the element draggable 89452 * @type Boolean 89453 */ 89454 this.setAttributeConfig('draggable', { 89455 value: attr.draggable || false, 89456 validator: YAHOO.lang.isBoolean, 89457 method: function(dd) { 89458 if (dd && this._wrap && !this.dd) { 89459 this._setupDragDrop(); 89460 } else { 89461 if (this.dd) { 89462 if (dd) { 89463 //activating an old DD instance.. 89464 D.addClass(this._wrap, this.CSS_DRAG); 89465 this.dd.DDM.regDragDrop(this.dd, "default"); 89466 } else { 89467 D.removeClass(this._wrap, this.CSS_DRAG); 89468 this.dd.unreg(); 89469 } 89470 } 89471 } 89472 } 89473 }); 89474 89475 /** 89476 * @attribute hover 89477 * @description Only show the handles when they are being moused over. 89478 * @type Boolean 89479 */ 89480 this.setAttributeConfig('hover', { 89481 value: attr.hover || false, 89482 validator: YAHOO.lang.isBoolean 89483 }); 89484 89485 /** 89486 * @attribute hiddenHandles 89487 * @description Don't show the handles, just use the cursor to the user. 89488 * @type Boolean 89489 */ 89490 this.setAttributeConfig('hiddenHandles', { 89491 value: attr.hiddenHandles || false, 89492 validator: YAHOO.lang.isBoolean 89493 }); 89494 89495 /** 89496 * @attribute knobHandles 89497 * @description Use the smaller handles, instead if the full size handles. 89498 * @type Boolean 89499 */ 89500 this.setAttributeConfig('knobHandles', { 89501 value: attr.knobHandles || false, 89502 validator: YAHOO.lang.isBoolean 89503 }); 89504 89505 /** 89506 * @attribute xTicks 89507 * @description The number of x ticks to span the resize to. 89508 * @type Number or False 89509 */ 89510 this.setAttributeConfig('xTicks', { 89511 value: attr.xTicks || false 89512 }); 89513 89514 /** 89515 * @attribute yTicks
89516 * @description The number of y ticks to span the resize to. 89517 * @type Number or False 89518 */ 89519 this.setAttributeConfig('yTicks', { 89520 value: attr.yTicks || false 89521 }); 89522 89523 /** 89524 * @attribute status 89525 * @description Show the status (new size) of the resize. 89526 * @type Boolean 89527 */ 89528 this.setAttributeConfig('status', { 89529 value: attr.status || false, 89530 validator: YAHOO.lang.isBoolean 89531 }); 89532 89533 /** 89534 * @attribute autoRatio 89535 * @description Using the shift key during a resize will toggle the ratio config. 89536 * @type Boolean 89537 */ 89538 this.setAttributeConfig('autoRatio', { 89539 value: attr.autoRatio || false, 89540 validator: YAHOO.lang.isBoolean 89541 }); 89542 89543 }, 89544 /** 89545 * @method destroy 89546 * @description Destroys the resize object and all of it's elements & listeners. 89547 */ 89548 destroy: function() { 89549 YAHOO.log('Destroying Resize', 'info', 'Resize'); 89550 for (var h in this._handles) { 89551 if (Lang.hasOwnProperty(this._handles, h)) { 89552 Event.purgeElement(this._handles[h]); 89553 this._handles[h].parentNode.removeChild(this._handles[h]); 89554 } 89555 } 89556 if (this._proxy) { 89557 this._proxy.parentNode.removeChild(this._proxy); 89558 } 89559 if (this._status) { 89560 this._status.parentNode.removeChild(this._status); 89561 } 89562 if (this.dd) { 89563 this.dd.unreg(); 89564 D.removeClass(this._wrap, this.CSS_DRAG); 89565 } 89566 if (this._wrap != this.get('element')) { 89567 this.setStyle('position', (this._positioned ? 'absolute' : 'relative')); 89568 this.setStyle('top', D.getStyle(this._wrap, 'top')); 89569 this.setStyle('left',D.getStyle(this._wrap, 'left')); 89570 this._wrap.parentNode.replaceChild(this.get('element'), this._wrap); 89571 } 89572 this.removeClass(this.CSS_RESIZE); 89573 89574 delete YAHOO.util.Resize._instances[this.get('id')]; 89575 //Brutal Object Destroy 89576 for (var i in this) { 89577 if (Lang.hasOwnProperty(this, i)) { 89578 this[i] = null; 89579 delete this[i]; 89580 } 89581 } 89582 }, 89583 /** 89584 * @method toString 89585 * @description Returns a string representing the Resize Object. 89586 * @return {String} 89587 */ 89588 toString: function() { 89589 if (this.get) { 89590 return 'Resize (#' + this.get('id') + ')'; 89591 } 89592 return 'Resize Utility'; 89593 } 89594 }); 89595 89596 YAHOO.util.Resize = Resize; 89597 89598/** 89599* @event dragEvent 89600* @description Fires when the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> dragEvent is fired for the config option draggable. 89601* @type YAHOO.util.CustomEvent 89602*/ 89603/** 89604* @event startResize 89605* @description Fires when a resize action is started. 89606* @type YAHOO.util.CustomEvent 89607*/ 89608/** 89609* @event endResize 89610* @description Fires when the mouseUp event from the Drag Instance fires. 89611* @type YAHOO.util.CustomEvent 89612*/ 89613/** 89614* @event resize 89615* @description Fires on every element resize (only fires once when used with proxy config setting). 89616* @type YAHOO.util.CustomEvent 89617*/ 89618/** 89619* @event beforeResize 89620* @description Fires before every element resize after the size calculations, returning false will stop the resize. 89621* @type YAHOO.util.CustomEvent 89622*/ 89623/** 89624* @event proxyResize 89625* @description Fires on every proxy resize (only fires when used with proxy config setting). 89626* @type YAHOO.util.CustomEvent 89627*/ 89628 89629})(); 89630 89631YAHOO.register("resize", YAHOO.util.Resize, {version: "2.9.0", build: "2800"}); 89632/* 89633Copyright (c) 2011, Yahoo! Inc. All rights reserved. 89634Code licensed under the BSD License: 89635http://developer.yahoo.com/yui/license.html 89636version: 2.9.0 89637*/ 89638/** 89639 * The Browser History Manager provides the ability to use the back/forward 89640 * navigation buttons in a DHTML application. It also allows a DHTML 89641 * application to be bookmarked in a specific state. 89642 * 89643 * This library requires the following static markup: 89644 * 89645 * <iframe id="yui-history-iframe" src="path-to-real-asset-in-same-domain"></iframe> 89646 * <input id="yui-history-field" type="hidden"> 89647 * 89648 * @module history 89649 * @requires yahoo,event 89650 * @namespace YAHOO.util 89651 * @title Browser History Manager 89652 */ 89653 89654/** 89655 * The History class provides the ability to use the back/forward navigation 89656 * buttons in a DHTML application. It also allows a DHTML application to 89657 * be bookmarked in a specific state. 89658 * 89659 * @class History 89660 * @constructor 89661 */ 89662YAHOO.util.History = (function () { 89663 89664 /** 89665 * Our hidden IFrame used to store the browsing history. 89666 * 89667 * @property _histFrame 89668 * @type HTMLIFrameElement 89669 * @default null 89670 * @private 89671 */ 89672 var _histFrame = null; 89673 89674 /** 89675 * INPUT field (with type="hidden" or type="text") or TEXTAREA. 89676 * This field keeps the value of the initial state, current state 89677 * the list of all states across pages within a single browser session. 89678 * 89679 * @property _stateField 89680 * @type HTMLInputElement|HTMLTextAreaElement 89681 * @default null 89682 * @private 89683 */ 89684 var _stateField = null; 89685 89686 /** 89687 * Flag used to tell whether YAHOO.util.History.initialize has been called. 89688 * 89689 * @property _initialized 89690 * @type boolean 89691 * @default false 89692 * @private 89693 */ 89694 var _initialized = false;
89695 89696 /** 89697 * List of registered modules. 89698 * 89699 * @property _modules 89700 * @type array 89701 * @default [] 89702 * @private 89703 */ 89704 var _modules = []; 89705 89706 /** 89707 * List of fully qualified states. This is used only by Safari. 89708 * 89709 * @property _fqstates 89710 * @type array 89711 * @default [] 89712 * @private 89713 */ 89714 var _fqstates = []; 89715 89716 /** 89717 * location.hash is a bit buggy on Opera. I have seen instances where 89718 * navigating the history using the back/forward buttons, and hence 89719 * changing the URL, would not change location.hash. That's ok, the 89720 * implementation of an equivalent is trivial. 89721 * 89722 * @method _getHash 89723 * @return {string} The hash portion of the document's location 89724 * @private 89725 */ 89726 function _getHash() { 89727 var i, href; 89728 href = self.location.href; 89729 i = href.indexOf("#"); 89730 return i >= 0 ? href.substr(i + 1) : null; 89731 } 89732 89733 /** 89734 * Stores all the registered modules' initial state and current state. 89735 * On Safari, we also store all the fully qualified states visited by 89736 * the application within a single browser session. The storage takes 89737 * place in the form field specified during initialization. 89738 * 89739 * @method _storeStates 89740 * @private 89741 */ 89742 function _storeStates() { 89743 89744 var moduleName, moduleObj, initialStates = [], currentStates = []; 89745 89746 for (moduleName in _modules) { 89747 if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) { 89748 moduleObj = _modules[moduleName]; 89749 initialStates.push(moduleName + "=" + moduleObj.initialState); 89750 currentStates.push(moduleName + "=" + moduleObj.currentState); 89751 } 89752 } 89753 89754 _stateField.value = initialStates.join("&") + "|" + currentStates.join("&"); 89755 } 89756 89757 /** 89758 * Sets the new currentState attribute of all modules depending on the new 89759 * fully qualified state. Also notifies the modules which current state has 89760 * changed. 89761 * 89762 * @method _handleFQStateChange 89763 * @param {string} fqstate Fully qualified state 89764 * @private 89765 */ 89766 function _handleFQStateChange(fqstate) { 89767 89768 var i, len, moduleName, moduleObj, modules, states, tokens, currentState; 89769 89770 if (!fqstate) { 89771 // Notifies all modules 89772 for (moduleName in _modules) { 89773 if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) { 89774 moduleObj = _modules[moduleName]; 89775 moduleObj.currentState = moduleObj.initialState; 89776 moduleObj.onStateChange(_decode(moduleObj.currentState)); 89777 } 89778 } 89779 return; 89780 } 89781 89782 modules = []; 89783 states = fqstate.split("&"); 89784 for (i = 0, len = states.length; i < len; i++) { 89785 tokens = states[i].split("="); 89786 if (tokens.length === 2) { 89787 moduleName = tokens[0]; 89788 currentState = tokens[1]; 89789 modules[moduleName] = currentState; 89790 } 89791 } 89792 89793 for (moduleName in _modules) { 89794 if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) { 89795 moduleObj = _modules[moduleName]; 89796 currentState = modules[moduleName]; 89797 if (!currentState || moduleObj.currentState !== currentState) { 89798 moduleObj.currentState = typeof currentState === 'undefined' ? moduleObj.initialState : currentState; 89799 moduleObj.onStateChange(_decode(moduleObj.currentState)); 89800 } 89801 } 89802 } 89803 } 89804 89805 /** 89806 * Update the IFrame with our new state. 89807 * 89808 * @method _updateIFrame 89809 * @private 89810 * @return {boolean} true if successful. false otherwise. 89811 */ 89812 function _updateIFrame (fqstate) { 89813 89814 var html, doc; 89815 89816 html = '<html><body><div id="state">' + 89817 YAHOO.lang.escapeHTML(fqstate) + 89818 '</div></body></html>'; 89819 89820 try { 89821 doc = _histFrame.contentWindow.document; 89822 doc.open(); 89823 doc.write(html); 89824 doc.close(); 89825 return true; 89826 } catch (e) { 89827 return false;
89828 } 89829 } 89830 89831 /** 89832 * Periodically checks whether our internal IFrame is ready to be used. 89833 * 89834 * @method _checkIframeLoaded 89835 * @private 89836 */ 89837 function _checkIframeLoaded() { 89838 89839 var doc, elem, fqstate, hash; 89840 89841 if (!_histFrame.contentWindow || !_histFrame.contentWindow.document) { 89842 // Check again in 10 msec... 89843 setTimeout(_checkIframeLoaded, 10); 89844 return; 89845 } 89846 89847 // Start the thread that will have the responsibility to 89848 // periodically check whether a navigate operation has been 89849 // requested on the main window. This will happen when 89850 // YAHOO.util.History.navigate has been called or after 89851 // the user has hit the back/forward button. 89852 89853 doc = _histFrame.contentWindow.document; 89854 elem = doc.getElementById("state"); 89855 // We must use innerText, and not innerHTML because our string contains 89856 // the "&" character (which would end up being escaped as "&") and 89857 // the string comparison would fail... 89858 fqstate = elem ? elem.innerText : null; 89859 89860 hash = _getHash(); 89861 89862 setInterval(function () { 89863 89864 var newfqstate, states, moduleName, moduleObj, newHash, historyLength; 89865 89866 doc = _histFrame.contentWindow.document; 89867 elem = doc.getElementById("state"); 89868 // See my comment above about using innerText instead of innerHTML... 89869 newfqstate = elem ? elem.innerText : null; 89870 89871 newHash = _getHash(); 89872 89873 if (newfqstate !== fqstate) { 89874 89875 fqstate = newfqstate; 89876 _handleFQStateChange(fqstate); 89877 89878 if (!fqstate) { 89879 states = []; 89880 for (moduleName in _modules) { 89881 if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) { 89882 moduleObj = _modules[moduleName]; 89883 states.push(moduleName + "=" + moduleObj.initialState); 89884 } 89885 } 89886 newHash = states.join("&"); 89887 } else { 89888 newHash = fqstate; 89889 } 89890 89891 // Allow the state to be bookmarked by setting the top window's 89892 // URL fragment identifier. Note that here, we are on IE, and 89893 // IE does not touch the browser history when setting the hash 89894 // (unlike all the other browsers). I used to write: 89895 // self.location.replace( "#" + hash ); 89896 // but this had a side effect when the page was not the top frame. 89897 self.location.hash = newHash; 89898 hash = newHash; 89899 89900 _storeStates(); 89901 89902 } else if (newHash !== hash) { 89903 89904 // The hash has changed. The user might have clicked on a link, 89905 // or modified the URL directly, or opened the same application 89906 // bookmarked in a specific state using a bookmark. However, we 89907 // know the hash change was not caused by a hit on the back or 89908 // forward buttons, or by a call to navigate() (because it would 89909 // have been handled above) We must handle these cases, which is 89910 // why we also need to keep track of hash changes on IE! 89911 89912 // Note that IE6 has some major issues with this kind of user 89913 // interaction (the history stack gets completely messed up) 89914 // but it seems to work fine on IE7. 89915 89916 hash = newHash; 89917 89918 // Now, store a new history entry. The following will cause the 89919 // code above to execute, doing all the dirty work for us... 89920 _updateIFrame(newHash); 89921 } 89922 89923 }, 50); 89924 89925 _initialized = true; 89926 YAHOO.util.History.onLoadEvent.fire(); 89927 } 89928 89929 /** 89930 * Finish up the initialization of the Browser History Manager. 89931 * 89932 * @method _initialize 89933 * @private 89934 */ 89935 function _initialize() { 89936 89937 var i, len, parts, tokens, moduleName, moduleObj, initialStates, initialState, currentStates, currentState, counter, hash; 89938 89939 // Decode the content of our storage field... 89940 parts = _stateField.value.split("|"); 89941 89942 if (parts.length > 1) { 89943 89944 initialStates = parts[0].split("&"); 89945 for (i = 0, len = initialStates.length; i < len; i++) { 89946 tokens = initialStates[i].split("="); 89947 if (tokens.length === 2) { 89948 moduleName = tokens[0]; 89949 initialState = tokens[1]; 89950 89951 moduleObj = YAHOO.lang.hasOwnProperty(_modules, moduleName) 89952 && _modules[moduleName]; 89953 89954 if (moduleObj) { 89955 moduleObj.initialState = initialState; 89956 } 89957 } 89958 } 89959 89960 currentStates = parts[1].split("&"); 89961 for (i = 0, len = currentStates.length; i < len; i++) { 89962 tokens = currentStates[i].split("="); 89963 if (tokens.length >= 2) { 89964 moduleName = tokens[0]; 89965 currentState = tokens[1]; 89966 89967 moduleObj = YAHOO.lang.hasOwnProperty(_modules, moduleName) 89968 && _modules[moduleName]; 89969 89970 if (moduleObj) { 89971 moduleObj.currentState = currentState; 89972 } 89973 } 89974 } 89975 } 89976 89977 if (parts.length > 2) { 89978 _fqstates = parts[2].split(","); 89979 } 89980 89981 if (YAHOO.env.ua.ie) { 89982 89983 if (typeof document.documentMode === "undefined" || document.documentMode < 8) { 89984 89985 // IE < 8 or IE8 in quirks mode or IE7 standards mode 89986 _checkIframeLoaded(); 89987 89988 } else { 89989 89990 // IE8 in IE8 standards mode 89991 YAHOO.util.Event.on(top, "hashchange", 89992 function () { 89993 var hash = _getHash(); 89994 _handleFQStateChange(hash); 89995 _storeStates(); 89996 }); 89997 89998 _initialized = true; 89999 YAHOO.util.History.onLoadEvent.fire(); 90000 90001 } 90002 90003 } else { 90004 90005 // Start the thread that will have the responsibility to 90006 // periodically check whether a navigate operation has been 90007 // requested on the main window. This will happen when 90008 // YAHOO.util.History.navigate has been called or after 90009 // the user has hit the back/forward button. 90010 90011 // On Gecko and Opera, we just need to watch the hash... 90012 hash = _getHash(); 90013 90014 setInterval(function () { 90015 90016 var state, newHash, newCounter; 90017 90018 newHash = _getHash(); 90019 if (newHash !== hash) { 90020 hash = newHash; 90021 _handleFQStateChange(hash); 90022 _storeStates(); 90023 } 90024 90025 }, 50); 90026 90027 _initialized = true; 90028 YAHOO.util.History.onLoadEvent.fire(); 90029 } 90030 } 90031 90032 /** 90033 * Wrapper around <code>decodeURIComponent()</code> that also converts + 90034 * chars into spaces. 90035 * 90036 * @method _decode 90037 * @param {String} string string to decode 90038 * @return {String} decoded string 90039 * @private 90040 * @since 2.9.0 90041 */ 90042 function _decode(string) { 90043 return decodeURIComponent(string.replace(/\+/g, ' ')); 90044 } 90045 90046 /** 90047 * Wrapper around <code>encodeURIComponent()</code> that converts spaces to 90048 * + chars. 90049 * 90050 * @method _encode 90051 * @param {String} string string to encode 90052 * @return {String} encoded string 90053 * @private 90054 * @since 2.9.0 90055 */ 90056 function _encode(string) { 90057 return encodeURIComponent(string).replace(/%20/g, '+'); 90058 } 90059 90060 return { 90061 90062 /** 90063 * Fired when the Browser History Manager is ready. If you subscribe to 90064 * this event after the Browser History Manager has been initialized, 90065 * it will not fire. Therefore, it is recommended to use the onReady 90066 * method instead. 90067 * 90068 * @event onLoadEvent 90069 * @see onReady 90070 */ 90071 onLoadEvent: new YAHOO.util.CustomEvent("onLoad"), 90072 90073 /** 90074 * Executes the supplied callback when the Browser History Manager is 90075 * ready. This will execute immediately if called after the Browser 90076 * History Manager onLoad event has fired. 90077 * 90078 * @method onReady 90079 * @param {function} fn what to execute when the Browser History Manager is ready. 90080 * @param {object} obj an optional object to be passed back as a parameter to fn. 90081 * @param {boolean|object} overrideContext If true, the obj passed in becomes fn's execution scope. 90082 * @see onLoadEvent 90083 */ 90084 onReady: function (fn, obj, overrideContext) { 90085 90086 if (_initialized) { 90087 90088 setTimeout(function () { 90089 var ctx = window; 90090 if (overrideContext) { 90091 if (overrideContext === true) { 90092 ctx = obj; 90093 } else { 90094 ctx = overrideContext; 90095 } 90096 } 90097 fn.call(ctx, "onLoad", [], obj); 90098 }, 0); 90099 90100 } else { 90101 90102 YAHOO.util.History.onLoadEvent.subscribe(fn, obj, overrideContext); 90103 90104 } 90105 }, 90106 90107 /** 90108 * Registers a new module. 90109 * 90110 * @method register 90111 * @param {string} module Non-empty string uniquely identifying the 90112 * module you wish to register. 90113 * @param {string} initialState The initial state of the specified 90114 * module corresponding to its earliest history entry. 90115 * @param {function} onStateChange Callback called when the 90116 * state of the specified module has changed. 90117 * @param {object} obj An arbitrary object that will be passed as a 90118 * parameter to the handler. 90119 * @param {boolean} overrideContext If true, the obj passed in becomes the 90120 * execution scope of the listener. 90121 */ 90122 register: function (module, initialState, onStateChange, obj, overrideContext) { 90123 90124 var scope, wrappedFn; 90125 90126 if (typeof module !== "string" || YAHOO.lang.trim(module) === "" || 90127 typeof initialState !== "string" || 90128 typeof onStateChange !== "function") { 90129 throw new Error("Missing or invalid argument"); 90130 } 90131 90132 if (YAHOO.lang.hasOwnProperty(_modules, module)) { 90133 // Here, we used to throw an exception. However, users have 90134 // complained about this behavior, so we now just return. 90135 return; 90136 } 90137 90138 // Note: A module CANNOT be registered after calling 90139 // YAHOO.util.History.initialize. Indeed, we set the initial state 90140 // of each registered module in YAHOO.util.History.initialize. 90141 // If you could register a module after initializing the Browser 90142 // History Manager, you would not read the correct state using 90143 // YAHOO.util.History.getCurrentState when coming back to the 90144 // page using the back button. 90145 if (_initialized) { 90146 throw new Error("All modules must be registered before calling YAHOO.util.History.initialize"); 90147 } 90148
90149 // Make sure the strings passed in do not contain our separators "," and "|" 90150 module = _encode(module); 90151 initialState = _encode(initialState); 90152 90153 // If the user chooses to override the scope, we use the 90154 // custom object passed in as the execution scope. 90155 scope = null; 90156 if (overrideContext === true) { 90157 scope = obj; 90158 } else { 90159 scope = overrideContext; 90160 } 90161 90162 wrappedFn = function (state) { 90163 return onStateChange.call(scope, state, obj); 90164 }; 90165 90166 _modules[module] = { 90167 name: module, 90168 initialState: initialState, 90169 currentState: initialState, 90170 onStateChange: wrappedFn 90171 }; 90172 }, 90173 90174 /** 90175 * Initializes the Browser History Manager. Call this method 90176 * from a script block located right after the opening body tag. 90177 * 90178 * @method initialize 90179 * @param {string|HTML Element} stateField <input type="hidden"> used 90180 * to store application states. Must be in the static markup. 90181 * @param {string|HTML Element} histFrame IFrame used to store 90182 * the history (only required on Internet Explorer) 90183 * @public 90184 */ 90185 initialize: function (stateField, histFrame) { 90186 90187 if (_initialized) { 90188 // The browser history manager has already been initialized. 90189 return; 90190 } 90191 90192 if (YAHOO.env.ua.opera && typeof history.navigationMode !== "undefined") { 90193 // Disable Opera's fast back/forward navigation mode and puts 90194 // it in compatible mode. This makes anchor-based history 90195 // navigation work after the page has been navigated away 90196 // from and re-activated, at the cost of slowing down 90197 // back/forward navigation to and from that page. 90198 history.navigationMode = "compatible"; 90199 } 90200 90201 if (typeof stateField === "string") { 90202 stateField = document.getElementById(stateField); 90203 } 90204 90205 if (!stateField || 90206 stateField.tagName.toUpperCase() !== "TEXTAREA" && 90207 (stateField.tagName.toUpperCase() !== "INPUT" || 90208 stateField.type !== "hidden" && 90209 stateField.type !== "text")) { 90210 throw new Error("Missing or invalid argument"); 90211 } 90212 90213 _stateField = stateField; 90214 90215 // IE < 8 or IE8 in quirks mode or IE7 standards mode 90216 if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) { 90217 90218 if (typeof histFrame === "string") { 90219 histFrame = document.getElementById(histFrame); 90220 } 90221 90222 if (!histFrame || histFrame.tagName.toUpperCase() !== "IFRAME") { 90223 throw new Error("Missing or invalid argument"); 90224 } 90225 90226 _histFrame = histFrame; 90227 } 90228 90229 // Note that the event utility MUST be included inline in the page. 90230 // If it gets loaded later (which you may want to do to improve the 90231 // loading speed of your site), the onDOMReady event never fires, 90232 // and the history library never gets fully initialized. 90233 YAHOO.util.Event.onDOMReady(_initialize); 90234 }, 90235 90236 /** 90237 * Call this method when you want to store a new entry in the browser's history. 90238 * 90239 * @method navigate 90240 * @param {string} module Non-empty string representing your module. 90241 * @param {string} state String representing the new state of the specified module. 90242 * @return {boolean} Indicates whether the new state was successfully added to the history. 90243 * @public 90244 */ 90245 navigate: function (module, state) { 90246 90247 var states; 90248 90249 if (typeof module !== "string" || typeof state !== "string") { 90250 throw new Error("Missing or invalid argument"); 90251 } 90252 90253 states = {}; 90254 states[module] = state; 90255 90256 return YAHOO.util.History.multiNavigate(states); 90257 }, 90258 90259 /** 90260 * Call this method when you want to store a new entry in the browser's history. 90261 * 90262 * @method multiNavigate 90263 * @param {object} states Associative array of module-state pairs to set simultaneously. 90264 * @return {boolean} Indicates whether the new state was successfully added to the history. 90265 * @public 90266 */ 90267 multiNavigate: function (states) { 90268 90269 var currentStates, moduleName, moduleObj, currentState, fqstate; 90270 90271 if (typeof states !== "object") { 90272 throw new Error("Missing or invalid argument"); 90273 } 90274 90275 if (!_initialized) { 90276 throw new Error("The Browser History Manager is not initialized"); 90277 } 90278 90279 for (moduleName in states) { 90280 if (!YAHOO.lang.hasOwnProperty(_modules, _encode(moduleName))) { 90281 throw new Error("The following module has not been registered: " + moduleName); 90282 } 90283 } 90284
90285 // Generate our new full state string mod1=xxx&mod2=yyy 90286 currentStates = []; 90287 90288 for (moduleName in _modules) { 90289 if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) { 90290 moduleObj = _modules[moduleName]; 90291 if (YAHOO.lang.hasOwnProperty(states, moduleName)) { 90292 currentState = states[_decode(moduleName)]; 90293 } else { 90294 currentState = _decode(moduleObj.currentState); 90295 } 90296 90297 // Make sure the strings passed in do not contain our separators "," and "|" 90298 moduleName = _encode(moduleName); 90299 currentState = _encode(currentState); 90300 90301 currentStates.push(moduleName + "=" + currentState); 90302 } 90303 } 90304 90305 fqstate = currentStates.join("&"); 90306 90307 if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) { 90308 90309 return _updateIFrame(fqstate); 90310 90311 } else { 90312 90313 // Known bug: On Safari 1.x and 2.0, if you have tab browsing 90314 // enabled, Safari will show an endless loading icon in the 90315 // tab. This has apparently been fixed in recent WebKit builds. 90316 // One work around found by Dav Glass is to submit a form that 90317 // points to the same document. This indeed works on Safari 1.x 90318 // and 2.0 but creates bigger problems on WebKit. So for now, 90319 // we'll consider this an acceptable bug, and hope that Apple 90320 // comes out with their next version of Safari very soon. 90321 self.location.hash = fqstate; 90322 90323 return true; 90324 } 90325 }, 90326 90327 /** 90328 * Returns the current state of the specified module. 90329 * 90330 * @method getCurrentState 90331 * @param {string} module Non-empty string representing your module. 90332 * @return {string} The current state of the specified module. 90333 * @public 90334 */ 90335 getCurrentState: function (module) { 90336 90337 var moduleObj; 90338 90339 if (typeof module !== "string") { 90340 throw new Error("Missing or invalid argument"); 90341 } 90342 90343 if (!_initialized) { 90344 throw new Error("The Browser History Manager is not initialized"); 90345 } 90346 90347 moduleObj = YAHOO.lang.hasOwnProperty(_modules, module) 90348 && _modules[module]; 90349 90350 if (!moduleObj) { 90351 throw new Error("No such registered module: " + module); 90352 } 90353 90354 return _decode(moduleObj.currentState); 90355 }, 90356 90357 /** 90358 * Returns the state of a module according to the URL fragment 90359 * identifier. This method is useful to initialize your modules 90360 * if your application was bookmarked from a particular state. 90361 * 90362 * @method getBookmarkedState 90363 * @param {string} module Non-empty string representing your module. 90364 * @return {string} The bookmarked state of the specified module. 90365 * @public 90366 */ 90367 getBookmarkedState: function (module) { 90368 90369 var i, len, idx, hash, states, tokens, moduleName; 90370 90371 if (typeof module !== "string") { 90372 throw new Error("Missing or invalid argument"); 90373 } 90374 90375 // Use location.href instead of location.hash which is already 90376 // URL-decoded, which creates problems if the state value 90377 // contained special characters... 90378 idx = self.location.href.indexOf("#"); 90379 if (idx >= 0) { 90380 hash = self.location.href.substr(idx + 1); 90381 states = hash.split("&"); 90382 for (i = 0, len = states.length; i < len; i++) { 90383 tokens = states[i].split("="); 90384 if (tokens.length === 2) { 90385 moduleName = tokens[0]; 90386 if (moduleName === module) { 90387 return _decode(tokens[1]); 90388 } 90389 } 90390 } 90391 } 90392 90393 return null; 90394 }, 90395 90396 /** 90397 * Returns the value of the specified query string parameter. 90398 * This method is not used internally by the Browser History Manager. 90399 * However, it is provided here as a helper since many applications 90400 * using the Browser History Manager will want to read the value of 90401 * url parameters to initialize themselves. 90402 * 90403 * @method getQueryStringParameter 90404 * @param {string} paramName Name of the parameter we want to look up. 90405 * @param {string} queryString Optional URL to look at. If not specified, 90406 * this method uses the URL in the address bar. 90407 * @return {string} The value of the specified parameter, or null. 90408 * @public 90409 */ 90410 getQueryStringParameter: function (paramName, url) { 90411 90412 var i, len, idx, queryString, params, tokens; 90413 90414 url = url || self.location.href; 90415 90416 idx = url.indexOf("?"); 90417 queryString = idx >= 0 ? url.substr(idx + 1) : url; 90418 90419 // Remove the hash if any 90420 idx = queryString.lastIndexOf("#"); 90421 queryString = idx >= 0 ? queryString.substr(0, idx) : queryString; 90422 90423 params = queryString.split("&"); 90424 90425 for (i = 0, len = params.length; i < len; i++) { 90426 tokens = params[i].split("="); 90427 if (tokens.length >= 2) { 90428 if (tokens[0] === paramName) { 90429 return _decode(tokens[1]); 90430 } 90431 } 90432 } 90433 90434 return null; 90435 } 90436 90437 }; 90438 90439})(); 90440YAHOO.register("history", YAHOO.util.History, {version: "2.9.0", build: "2800"}); 90441/** 90442 * Patch to Menu to allow empty groups to remain in the menu structure. 90443 * Required by: Sites dynamic drop-down menu. 90444 * Patches: YUI 2.6.0 to 2.9.0 90445 * Escalated: Yes, but closed as "by design" 90446 */ 90447(function() 90448{ 90449 var Lang = YAHOO.lang, 90450 Dom = YAHOO.util.Dom, 90451 _FIRST_OF_TYPE = "first-of-type"; 90452
90453 YAHOO.widget.Menu.prototype._removeItemFromGroupByIndex = function (p_nGroupIndex, p_nItemIndex, p_keepEmptyGroup) { 90454 90455 var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0, 90456 aGroup = this._getItemGroup(nGroupIndex), 90457 aArray, 90458 oItem, 90459 oUL; 90460 90461 if (aGroup) { 90462 90463 aArray = aGroup.splice(p_nItemIndex, 1); 90464 oItem = aArray[0]; 90465 90466 if (oItem) { 90467 90468 // Update the index and className properties of each member 90469 this._updateItemProperties(nGroupIndex); 90470 90471 if (aGroup.length === 0 && !p_keepEmptyGroup) { 90472 90473 // Remove the UL 90474 oUL = this._aListElements[nGroupIndex]; 90475 90476 if (oUL && oUL.parentNode) { 90477 oUL.parentNode.removeChild(oUL); 90478 } 90479 90480 // Remove the group from the array of items 90481 this._aItemGroups.splice(nGroupIndex, 1); 90482 90483 // Remove the UL from the array of ULs 90484 this._aListElements.splice(nGroupIndex, 1); 90485 90486 /* 90487 Assign the "first-of-type" class to the new first UL 90488 in the collection 90489 */ 90490 oUL = this._aListElements[0]; 90491 90492 if (oUL) { 90493 Dom.addClass(oUL, _FIRST_OF_TYPE); 90494 } 90495 } 90496 90497 this.itemRemovedEvent.fire(oItem); 90498 this.changeContentEvent.fire(); 90499 } 90500 } 90501 90502 // Return a reference to the item that was removed 90503 return oItem; 90504 }; 90505 90506 YAHOO.widget.Menu.prototype._removeItemFromGroupByValue = function (p_nGroupIndex, p_oItem, p_keepEmptyGroup) { 90507 90508 var aGroup = this._getItemGroup(p_nGroupIndex), 90509 nItems, 90510 nItemIndex, 90511 returnVal, 90512 i; 90513 90514 if (aGroup) { 90515 nItems = aGroup.length; 90516 nItemIndex = -1; 90517 90518 if (nItems > 0) { 90519 i = nItems-1; 90520 do { 90521 if (aGroup[i] == p_oItem) { 90522 nItemIndex = i; 90523 break; 90524 } 90525 } 90526 while (i--); 90527 90528 if (nItemIndex > -1) { 90529 returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex, p_keepEmptyGroup); 90530 } 90531 } 90532 } 90533 return returnVal; 90534 }; 90535 90536 YAHOO.widget.Menu.prototype.removeItem = function (p_oObject, p_nGroupIndex, p_keepEmptyGroup) { 90537 var oItem, 90538 returnVal; 90539 90540 if (!Lang.isUndefined(p_oObject)) { 90541 if (p_oObject instanceof YAHOO.widget.MenuItem) { 90542 oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject, p_keepEmptyGroup); 90543 } 90544 else if (Lang.isNumber(p_oObject)) { 90545 oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject, p_keepEmptyGroup); 90546 } 90547 90548 if (oItem) { 90549 oItem.destroy(); 90550 returnVal = oItem; 90551 } 90552 } 90553 90554 return returnVal; 90555 }; 90556})(); 90557 90558/** 90559 * Patch to Container to prevent IE6 trying to set properties on elements that have been removed from the DOM. 90560 * This function is called via a setTimer(), so this patch fixes a race condition. 90561 * Required by: Document List "Loading Document Library..." pop-up. 90562 * Patches: YUI 2.7.0 to 2.9.0 90563 */ 90564(function() 90565{ 90566 /** 90567 * Adjusts the size of the shadow based on the size of the element. 90568 * @method sizeUnderlay 90569 */ 90570 YAHOO.widget.Panel.prototype.sizeUnderlay = function() 90571 { 90572 var oUnderlay = this.underlay, 90573 oElement; 90574 90575 if (oUnderlay) { 90576 oElement = this.element; 90577 if (oElement) { 90578 oUnderlay.style.width = oElement.offsetWidth + "px"; 90579 oUnderlay.style.height = oElement.offsetHeight + "px"; 90580 } 90581 } 90582 }; 90583})(); 90584 90585 90586(function() 90587{ 90588 /** 90589 * Drag drop support for ipad (safari) & android (default browser & chrome) making yui's drag n drop classes work out of the box. 90590 * 90591 * The trick is to: 90592 * - Stop listening for mouse events: "mousedown", "mousemove" & "mouseup" 90593 * - Start listening to touch events: "touchstart", "touchmove" & "touchend" 90594 * - Make sure all events have with pageX & pageY attributes set so they can be treated as a "mouse" event. 90595 * 90596 * Note! Assumes the following when invoked: 90597 * - the YAHOO.util.DragDropMgr && YAHOO.util.DragDrop classes have been loaded 90598 * - the YAHOO.util.DragDropMgr have been initialized 90599 */ 90600 if ((YAHOO.env.ua.ipad || YAHOO.env.ua.android) && YAHOO.util.DragDropMgr && YAHOO.util.DragDrop) 90601 {
90602 var Event = YAHOO.util.Event; 90603 90604 // Fake an object that pretends to be an event so we can set it's pageX & pageY coords 90605 var createMouseEvent = function(e, preventDefault, stopPropagation) 90606 { 90607 var event = { 90608 type: e.type, 90609 target: YAHOO.util.Event.getTarget(e), 90610 pageX: e.pageX, 90611 pageY: e.pageY, 90612 which: e.which 90613 }; 90614 90615 // Make sure the event can stop bubbling 90616 var orgEvent = e, 90617 pd = YAHOO.lang.isBoolean(preventDefault) ? preventDefault : true, 90618 sp = YAHOO.lang.isBoolean(stopPropagation) ? stopPropagation : true; 90619 if (e.preventDefault) 90620 { 90621 event.preventDefault = function() 90622 { 90623 if (pd) 90624 { 90625 orgEvent.preventDefault(); 90626 } 90627 } 90628 } 90629 if (e.stopPropagation) 90630 { 90631 event.stopPropagation = function() 90632 { 90633 if (sp) 90634 { 90635 orgEvent.stopPropagation(); 90636 } 90637 }; 90638 } 90639 90640 // Android always sets the pageY but the pageX is always 0, pick it from the targetTouches instead 90641 if (e.targetTouches && e.targetTouches.length > 0) 90642 { 90643 var touch = e.targetTouches[e.targetTouches.length - 1]; 90644 event.pageX = touch.pageX; 90645 event.pageY = touch.pageY; 90646 } 90647 90648 // Add it in since the yui classes are looking at the value 90649 if (e.clientX || e.clientY) 90650 { 90651 event.clientX = e.clientX; 90652 event.clientY = e.clientY; 90653 } 90654 90655 // Add it in since the yui classes are looking at the value 90656 if (e.button) 90657 { 90658 event.button = e.button; 90659 } 90660 90661 return event; 90662 }; 90663 90664 // First patch the YAHOO.uti.DragDropMgr (which is an already created singleton object without a prototype) 90665 var DDM_patch = function() 90666 { 90667 // Remove the mouse listeners that was added in DragDropMgr.onLoad 90668 Event.removeListener(document, "mouseup", this.handleMouseUp); 90669 Event.removeListener(document, "mousemove", this.handleMouseMove); 90670 90671 // Add in a "proxy" mousemove listener 90672 var original_handleMouseMove = this.handleMouseMove; 90673 this.handleMouseMove = function(e) 90674 { 90675 // Create a faked event so that pageX and pageY will be set 90676 var event = createMouseEvent(e); 90677 original_handleMouseMove.call(this, event); 90678 90679 // Make sure to save the touch coords since the "touchend" event always have pageX and pageY set to 0 90680 this._lastTouchPageX = event.pageX; 90681 this._lastTouchPageY = event.pageY; 90682 }; 90683 90684 // Add in a "proxy" mouseup listener 90685 var original_handleMouseUp = this.handleMouseUp; 90686 this.handleMouseUp = function(e) 90687 { 90688 // Create a faked event so that we can make sure event propagation isn't stopped 90689 var event = createMouseEvent(e, false, false); 90690 original_handleMouseUp.call(this, event); 90691 }; 90692 90693 // Add our own proxy touch listeners 90694 Event.on(document, "touchend", this.handleMouseUp, this, true); 90695 Event.on(document, "touchmove", this.handleMouseMove, this, true); 90696 90697 // Make sure the shim is listening to touch events instead of mouse events 90698 var original__createShim = this._createShim; 90699 this._createShim = function() 90700 { 90701 original__createShim.call(this); 90702 90703 // Stop listening to mouse events 90704 Event.removeListener(s, "mouseup", this.handleMouseUp); 90705 Event.removeListener(s, "mouseover", this.handleMouseMove); 90706 90707 // Start listening to touch events 90708 Event.on(s, "touchend", this.handleMouseUp, this, true); 90709 Event.on(s, "touchmove", this.handleMouseMove, this, true); 90710 }; 90711 90712 // Patch the fire events method so we it can treat the "touchend" event as a "mouseup" event (having pageX & pageY) 90713 var original_fireEvents = this.fireEvents; 90714 this.fireEvents = function(e, isDrop) 90715 { 90716 var event = e; 90717 if (isDrop) 90718 { 90719 // Create a fake event object with all attributes the drag drop classes seem to use 90720 event = createMouseEvent(e, false, false); 90721 event.pageX = this._lastTouchPageX; 90722 event.pageY = this._lastTouchPageY; 90723 } 90724 90725 original_fireEvents.call(this, event, isDrop); 90726 }; 90727 90728 }; 90729 DDM_patch.call(YAHOO.util.DragDropMgr); 90730 90731 // Now patch the YAHOO.util.DragDrop prototype 90732 var DD_patch = function() 90733 { 90734 var original_init = YAHOO.util.DragDrop.prototype.init; 90735 YAHOO.util.DragDrop.prototype.init = function(id, sGroup, config) 90736 { 90737 original_init.call(this, id, sGroup, config); 90738 90739 // Stop listening to mouse events 90740 Event.removeListener(this._domRef || this.id, "mousedown", this.handleMouseDown); 90741 90742 // Start listening to touch events 90743 Event.on(this._domRef || this.id, "touchstart", this.handleMouseDown, this, true); 90744 }; 90745 90746 var original_setOuterHandleElId = YAHOO.util.DragDrop.prototype.setOuterHandleElId; 90747 YAHOO.util.DragDrop.prototype.setOuterHandleElId = function(id) 90748 { 90749 original_setOuterHandleElId.call(this, id); 90750 90751 // Stop listening to mouse events 90752 Event.removeListener(id, "mousedown", this.handleMouseDown); 90753 90754 // Start listening to touch events 90755 Event.on(id, "touchstart", this.handleMouseDown, this, true); 90756 }; 90757 90758 var original_handleMouseDown = YAHOO.util.DragDrop.prototype.handleMouseDown; 90759 YAHOO.util.DragDrop.prototype.handleMouseDown = function(e, oDD) 90760 { 90761 // Create a faked event with pageX and pageY attributes to keep yui happy 90762 var event = createMouseEvent(e, false, false); 90763 90764 // Make sure to save the touch coords since the "touchend" event al
90764ways have pageX and pageY set to 0 90765 this._lastTouchPageX = event.pageX; 90766 this._lastTouchPageY = event.pageY; 90767 90768 original_handleMouseDown.call(this, event, oDD); 90769 }; 90770 }; 90771 DD_patch.call(YAHOO.util.DragDrop); 90772 90773 } 90774})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.