Index: openacs-4/packages/ajaxhelper/ajaxhelper.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/ajaxhelper.info,v diff -u -r1.4 -r1.5 --- openacs-4/packages/ajaxhelper/ajaxhelper.info 20 Apr 2006 12:31:27 -0000 1.4 +++ openacs-4/packages/ajaxhelper/ajaxhelper.info 20 Aug 2006 14:39:20 -0000 1.5 @@ -8,13 +8,13 @@ t ajax - + Hamilton Chua - Provides helper procs to generate javascript for Ajax and cinematic effects. Include Scriptaculous (1.6.1) Javascript Libraries and the Yahoo UI Libraries. + Provides helper procs to generate javascript used for Ajax and generating cinematic effects. Includes Scriptaculous (1.6.2) Javascript Libraries and the Yahoo UI Libraries (0.11.2). Solution Grove 0 - + Index: openacs-4/packages/ajaxhelper/tcl/ajax-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/tcl/ajax-procs.tcl,v diff -u -r1.3 -r1.4 --- openacs-4/packages/ajaxhelper/tcl/ajax-procs.tcl 5 Apr 2006 06:38:41 -0000 1.3 +++ openacs-4/packages/ajaxhelper/tcl/ajax-procs.tcl 20 Aug 2006 14:39:20 -0000 1.4 @@ -1,8 +1,7 @@ ad_library { Library for Ajax Helper Procs - based on - Scriptaculous and Prototype for Ajax and Effects + based on Scriptaculous and Prototype for Ajax and Effects OverlibMWS for Popups @author Hamilton Chua (ham@solutiongrove.com) @@ -14,7 +13,7 @@ ad_proc -private ah::get_package_id { } { - Return the package_id of the installed and mounted ajax helper + Return the package_id of the installed and mounted ajax helper instance @author Hamilton Chua (ham@solutiongrove.com) @creation-date 2006-01-16 @@ -47,87 +46,94 @@ Receives a string and surrounds it with single quotes. This is a utility proc used to make a parameter passed to a proc a string. The assumption is that an element passed as a parameter is a javascript variable. + + @author Hamilton Chua (ham@solutiongrove.com) + @creation-date 2006-01-16 + @return } { return "'$element'" } -ad_proc -public ah::js_sources { - {-default:boolean} - {-source ""} - {-withbubble:boolean} - {-scrollable:boolean} - {-draggable:boolean} -} { +ad_proc -private ah::dynamic_load_functions { - Generates the < script > syntax needed on the head - for each set of javascript files this package uses or needs to source. - The code : -
-		[ah::js_sources -default]
-	
- will load the prototype javascript library and scriptaculous javascript files by default. +} { + Generates the javascript functions that perform dynamic loading of local javascript files. + http://www.phpied.com/javascript-include/ + WARNING : experimental - It's not recommended to mix scriptaculous and rico. - @author Hamilton Chua (ham@solutiongrove.com) - @creation-date 2006-01-16 + @creation-date 2006-04-20 - @param default Loads the prototype and scriptaculous javascript libraries. - @param source The caller can specify which set of javascript source files to load. - Valid values include - "rounder" : to load the rico corner rounder functions only, use this if you are working primarily with scriptaculous, - "rico" : to load the rico javascript library, - "overlibws" : to load the overlibmws javascript files for dhtml callouts and popups. +} { + set ah_base_url [ah::get_url] + set script "" + return $script +} - @param withbubble Do we want to use the overlibmws plugin for bubble callouts ? - @param scrollable Do we want to use the overlibmws scrollable plugin ? - @param draggable Do we want to use the overlibmws draggable plugin ? +ad_proc -public ah::js_include { + {-js_file ""} +} { + Generates the javscript to include a js file dynamically via DOM to the head section of the page. + WARNING : experimental - @return + @author Hamilton Chua (ham@solutiongrove.com) + @creation-date 2006-04-20 +} { + return "js_include_once('$js_file'); " +} - @error +ad_proc -public ah::js_source_dynamic { + {-js "default"} + {-enclose:boolean} +} { + Uses the javascript dynamic loading functions to load the comma separated list of javascript source file. + WARNING : experimental + @author Hamilton Chua (ham@solutiongrove.com) + @creation-date 2006-04-20 + + + @param js A comma separated list of js files to load. + Possible values include prototype, scriptaculous, rounder, rico, overlibmws, overlibmws_bubble, overlibmws_scroll, overlibmws_drag + @param enclose Specify this if you want the javascript to be enclosed in script tags, which is usually the case unless you include this along with other javascript. } { + set ah_base_url [ah::get_url] set script "" - - if { $default_p } { - # load prototype and scriptaculous js files - append script " \n" - append script " \n" - } else { - if { [info exists source] } { - # load other js libraries - switch $source { - "rico" { - append script " \n" - } - "rounder" { - append script " \n" - } - "overlibmws" { - append script " \n" - append script "\n" - if { $withbubble_p } { - append script "\n" - append script "\n" - } - if { $scrollable_p } { - append script "\n" - } - if { $draggable_p } { - append script "\n" - } - } - default { - # invalid value for source - } + set js_file_list [split $js ","] + + foreach x $js_file_list { + switch $x { + "rico" { + append script [ah::js_include -js_file "${ah_base_url}rico/rico.js"] } + "rounder" { + append script [ah::js_include -js_file "${ah_base_url}rico/rico.js"] + append script [ah::js_include -js_file "${ah_base_url}rico/rounder.js"] + } + "overlibmws" { + append script [ah::js_include -js_file "${ah_base_url}overlibmws/overlibmws.js"] + append script [ah::js_include -js_file "${ah_base_url}overlibmws/overlibmws_overtwo.js"] + } + "overlibmws_bubble" { + append script [ah::js_include -js_file "${ah_base_url}overlibmws/overlibmws_bubble.js"] + } + "overlibmws_scroll" { + append script [ah::js_include -js_file "${ah_base_url}overlibmws/overlibmws_scroll.js"] + } + "overlibmws_drag" { + append script [ah::js_include -js_file "${ah_base_url}overlibmws/overlibmws_draggable.js"] + } + default { + append script [ah::js_include -js_file "${ah_base_url}prototype/prototype.js"] + append script [ah::js_include -js_file "${ah_base_url}scriptaculous/scriptaculous.js"] + } } } - return $script + if { $enclose_p } { set script [ah::enclose_in_script -script ${script} ] } + return $script } ad_proc -private ah::enclose_in_script { @@ -139,7 +145,7 @@ @param script string to enclose in javascript tags. } { - set tag "" return $tag @@ -154,6 +160,7 @@ } { Use prototype's Event object to watch/listen to a specific event from a specific html element. Valid events include click, load, mouseover etc. + See ah::yui::addlistener for Yahoo's implementation which some say is more superior. @author Hamilton Chua (ham@solutiongrove.com) @creation-date 2006-02-28 @@ -196,6 +203,7 @@ ad_proc -public ah::ajaxrequest { -url:required + {-asynchronous "true"} {-pars ""} {-options ""} } { @@ -212,9 +220,10 @@ @param url the url that the javascript will call/query @param pars the parameters that will be passed to Ajax.Request. these parameters should normally be enclosed in single quotes ('') unless you intend to provide a javascript variable or function as a parameter @param options the options that will be passed to the Ajax.Request javascript function + @param asynchronous the default is true } { - set preoptions "asynchronous:'true',method:'post'" + set preoptions "asynchronous:${asynchronous},method:'post'" if { [exists_and_not_null pars] } { append preoptions ",parameters:$pars" @@ -228,6 +237,7 @@ ad_proc -public ah::ajaxupdate { -container:required -url:required + {-asynchronous "true"} {-pars ""} {-options ""} {-effect ""} @@ -236,7 +246,7 @@ {-container_is_var:boolean} } { Generate an Ajax.Updater javascript object. - The parameters are passed directly to Ajax.Update script. + The parameters are passed directly to the Ajax.Update script. You can optionally specify an effect to use as the container is updated. By default it will use the "Appear" effect. Parameters and options are case sensitive, refer to scriptaculous documentation. @@ -268,7 +278,7 @@ set container [ah::isnot_js_var $container] } - set preoptions "asynchronous:'true',method:'post'" + set preoptions "asynchronous:$asynchronous,method:'post'" if { [exists_and_not_null pars] } { append preoptions ",parameters:$pars" @@ -296,6 +306,7 @@ The ah::source must be executed with -source "overlibmws" For more information about the options that you can pass http://www.macridesweb.com/oltest/ + See ah::yui::tooltip for Yahoo's implementation @author Hamilton Chua (ham@solutiongrove.com) @creation-date 2006-02-12 @@ -346,7 +357,7 @@ This proc will generate mouseover and mouseout javascript for dhtml callout or popup using overlibmws and the overlibmws bubble plugin. - The ah::source must be called with -source "overlibmws" -withbubble + The ah::source must be called with -source "overlibmws,overlibmws_bubble" @author Hamilton Chua (ham@solutiongrove.com) @creation-date 2006-01-16 @@ -356,7 +367,6 @@ @param textsize the size of the text in the popup @return - @error } { @@ -382,6 +392,9 @@ @param url the url to make the xmlhttp call to @param pars the parameters in querystring format you want to pass to the url + @param options the options you want to pass to overlibmws + @param type parameter specific to the bubble callout + @param textsize the size of the text in the callout @return @@ -411,10 +424,10 @@ @param element the page element that you want to apply the effect to @param effect specify one of the scriptaculous effects you want to implement - @param options specify the options to pass to the javascript + @param options specify the options to pass to the scritpaculous javascript + @param element_is_var specify this if the element you are passing is a javascript variable @return - @error } { @@ -431,7 +444,7 @@ {-options ""} {-element_is_var:boolean} } { - Generates javascript that toggle the state of an element. + Generates javascript that toggles the state of an element. The parameters are passed directly to the scriptaculous toggle script. Parameters and options are case sensitive, refer to scriptaculous documentation. http://wiki.script.aculo.us/scriptaculous/show/Effect.toggle @@ -441,9 +454,10 @@ @param element the page element that you want to apply the effect to @param effect specify one of the scriptaculous effects you want to toggle - - @return - + @param options specify the options to pass to the scritpaculous javascript + @param element_is_var specify this if the element you are passing is a javascript variable + + @return @error } { @@ -481,9 +495,7 @@ if { !$element_is_var_p } { set element [ah::isnot_js_var $element] } - set script "new Draggable \($element,\{$options\}\);" - return $script } @@ -534,9 +546,9 @@ @creation-date 2006-02-24 @param element the page element that you want to be a droppable + @param element_is_var specify this parameter if the element you are passing is a javscript variable @return - @error } { @@ -563,10 +575,11 @@ @param element the page element that you want to apply the effect to @param options specify the scriptaculous options + @param element_is_var specify this parameter if the element you are passing is a javscript variable @return - @error + } { if { !$element_is_var_p } { set element [ah::isnot_js_var $element] @@ -598,4 +611,62 @@ } set script "Rico.Corner.round\($element, \{$options\}\); " return $script +} + +ad_proc -public ah::js_sources { + {-source "default"} +} { + + Will load the prototype javascript library and scriptaculous javascript files. + + @author Hamilton Chua (ham@solutiongrove.com) + @creation-date 2006-01-16 + + @param source The caller can specify which set of javascript source files to load. This can be a comma seprated list + Valid values include + "default" : to load prototype and scriptaculous libraries + "rounder" : to load the rico corner rounder functions only, use this if you are working primarily with scriptaculous, + "rico" : to load the rico javascript library, + "overlibmws" : to load the overlibmws javascript files for dhtml callouts and popups. + "overlibmws_bubble" : to load the overlibmws javascript files for dhtml callouts and popups. + "overlibmws_scroll" : to load the overlibmws javascript files for dhtml bubble callouts and popups that scroll. + "overlibmws_drag" : to load the overlibmws javascript files for draggable dhtml callouts and popups. + + @return + @error +} { + + set ah_base_url [ah::get_url] + set js_file_list [split $source ","] + set script "" + + foreach x $js_file_list { + switch $x { + "rico" { + append script " \n" + } + "rounder" { + append script " \n" } + "overlibmws" { + append script " \n" + append script "\n" + } + "overlibmws_bubble" { + append script "\n" + append script "\n" + } + "overlibmws_scroll" { + append script "\n" + } + "overlibmws_drag" { + append script "\n" + } + default { + append script " \n" + append script " \n" + } + } + } + + return $script } \ No newline at end of file Index: openacs-4/packages/ajaxhelper/tcl/ajax-yahoo-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/tcl/ajax-yahoo-procs.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/ajaxhelper/tcl/ajax-yahoo-procs.tcl 5 Apr 2006 06:38:41 -0000 1.1 +++ openacs-4/packages/ajaxhelper/tcl/ajax-yahoo-procs.tcl 20 Aug 2006 14:39:20 -0000 1.2 @@ -7,26 +7,84 @@ @creation-date 2006-01-16 } -namespace eval ah::yahoo { } +namespace eval ah::yui { } -ad_proc -public ah::yahoo::js_sources { - {-default:boolean} - {-source ""} +ad_proc -public ah::yui::js_source_dynamic { + {-js "default"} + {-enclose:boolean} } { + Dynamically Loads the Yahoo UI javascript libraries. + WARNING : experimental, use ah::yui::js_sources instead + + + @author Hamilton Chua (ham@solutiongrove.com) + @creation-date 2006-04-20 + + @param js Comma separated list of javascript files to load + Valid values include + "default" : loads yui.js and dom.js, the most commonly used + "animation" : loads js for animation + "event" : loads js for event monitoring (e.g. listnern) + "treeview" : loads js for Yahoo's Tree View control + "calendar" : loads js for Yahoo's Calendar Control + "dragdrop" : loads js for Yahoo's Drag and Drop functions + "slider" : loads js for slider functions + +} { + + set ah_base_url [ah::get_url] + set script "" + set js_file_list [split $js ","] + foreach x $js_file_list { + switch $x { + "animation" { + append script [ah::js_include -js_file "${ah_base_url}yui/animation/animation.js"] + } + "event" { + append script [ah::js_include -js_file "${ah_base_url}yui/event/event.js"] + } + "treeview" { + append script [ah::js_include -js_file "${ah_base_url}yui/treeview/treeview.js"] + } + "calendar" { + append script [ah::js_include -js_file "${ah_base_url}yui/calendar/calendar.js"] + } + "dragdrop" { + append script [ah::js_include -js_file "${ah_base_url}yui/dragdrop/dragdrop.js"] + } + "slider" { + append script [ah::js_include -js_file "${ah_base_url}yui/slider/slider.js"] + } + default { + append script [ah::js_include -js_file "${ah_base_url}yui/yui.js"] + append script [ah::js_include -js_file "${ah_base_url}yui/dom/dom.js"] + } + } + } + + if { $enclose_p } { set script [ah::enclose_in_script -script ${script} ] } + + return $script +} + +ad_proc -public ah::yui::js_sources { + {-source "default"} +} { + Generates the < script > syntax needed on the head - for Yahoo's User Interface Library + for yui's User Interface Library The code :
-		[ah::yahoo::js_sources -default]
+		[ah::yui::js_sources -default]
 	
will load the default YUI javascript library which includes the connections and doms js files @author Hamilton Chua (ham@solutiongrove.com) @creation-date 2006-01-16 @param default Loads the prototype and scriptaculous javascript libraries. - @param source The caller can specify which set of javascript source files to load. + @param source The caller can specify which set of javascript source files to load. You can specify more than one by separating the list with commas. Valid values include "animation" : loads animation.js "event" : loads events.js @@ -36,50 +94,48 @@ "slider" : loads slider.js @return - @error } { set ah_base_url [ah::get_url] set script "" - - if { $default_p } { - append script " \n" - append script " \n" - } else { - if { [info exists source] } { - # load other js libraries - switch $source { - "animation" { - append script " \n" - } - "event" { - append script " \n" - } - "treeview" { - append script " \n" - } - "calendar" { - append script " \n" - } - "dragdrop" { - append script " \n" - } - "slider" { - append script " \n" - } - - default { - # invalid value for source - } + set js_file_list [split $source ","] + + foreach x $js_file_list { + switch $x { + "animation" { + append script " \n" } + "event" { + append script " \n" + } + "treeview" { + append script " \n" + } + "calendar" { + append script " \n" + } + "dragdrop" { + append script " \n" + } + "slider" { + append script " \n" + } + "container" { + append script " \n" + append script " \n" + } + default { + append script " \n" + append script " \n" + } } } return $script } -ad_proc -public ah::yahoo::addlistener { +ad_proc -public ah::yui::addlistener { -element:required -event:required {-callback ""} @@ -91,4 +147,19 @@ set element [ah::isnot_js_var $element] } return "YAHOO.util.Event.addListener($element,\"$event\",${callback});\n" +} + +ad_proc -public ah::yui::tooltip { + -varname:required + -element:required + -message:required + {-enclose:boolean} + {-options ""} +} { + Generates the javascript to create a tooltip using yahoo's user interface javascript library. + For this to work, the default and container sources need to be loaded, see ah::yui::js_sources +} { + set script "var $varname = new YAHOO.widget.Tooltip(\"alertTip\", { context:\"$element\", text:\"$message\", $options });" + if { $enclose_p } { set script [ah::enclose_in_script -script ${script} ] } + return $script } \ No newline at end of file Index: openacs-4/packages/ajaxhelper/www/dynamicInclude.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/Attic/dynamicInclude.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/dynamicInclude.js 20 Aug 2006 14:39:21 -0000 1.1 @@ -0,0 +1,155 @@ +//Javascript dynamic Include Library +//Code derived from: +//Author: Stoyan Stefanov +//SITE: www.phpied.com +//email: ssttoo at gmaildotcom +//mad props due.. +//I made the manager objects and added the css methods so +//I can use this library for both +//me: Greg Patmore +//hit me up: //greg at ito-ydotcom + + +//js + +//holds the currently loaded .js libraries +//NOTE: idiot alert! any files included outside these methods cannot be +// referenced by this array +var js_includes = new Array(); + + +//use this manager to keep things simple +function JSManager(){ + this.included = js_includes; + this.include = js_include_once; + this.remove = js_remove_include; +} + +function js_include_dom(script_filename) { + var html_doc = document.getElementsByTagName('head').item(0); + var js = document.createElement('script'); + js.setAttribute('language', 'javascript'); + js.setAttribute('type', 'text/javascript'); + js.setAttribute('src', script_filename); + html_doc.appendChild(js); + return false; +} + +function js_include_once(script_filename) { + if (!in_array(script_filename, js_includes)) { + js_includes[js_includes.length] = script_filename; + js_include_dom(script_filename); + } +} + +//"un-includes" the included js file from the document +//use sparingly, as it's a memory whore +//NOTE: will not kill any timeouts you may have going +// but the script will be removed from the rendered source +function js_remove_include(scriptname){ + var docScripts = document.getElementsByTagName('script'); + for(var i=0;i 0) this.index-- else this.index = this.entryCount-1; + this.getEntry(this.index).scrollIntoView(true); }, markNext: function() { if(this.index < this.entryCount-1) this.index++ else this.index = 0; + this.getEntry(this.index).scrollIntoView(false); }, getEntry: function(index) { @@ -531,7 +536,7 @@ Element.hide(this.element); this.createForm(); this.element.parentNode.insertBefore(this.form, this.element); - Field.scrollFreeActivate(this.editField); + if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); // stop the event to avoid a page refresh in Safari if (evt) { Event.stop(evt); @@ -636,6 +641,7 @@ Element.removeClassName(this.form, this.options.loadingClassName); this.editField.disabled = false; this.editField.value = transport.responseText.stripTags(); + Field.scrollFreeActivate(this.editField); }, onclickCancel: function() { this.onComplete(); Index: openacs-4/packages/ajaxhelper/www/scriptaculous/dragdrop.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/scriptaculous/Attic/dragdrop.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/ajaxhelper/www/scriptaculous/dragdrop.js 20 Apr 2006 12:31:28 -0000 1.3 +++ openacs-4/packages/ajaxhelper/www/scriptaculous/dragdrop.js 20 Aug 2006 14:39:23 -0000 1.4 @@ -5,6 +5,9 @@ /*--------------------------------------------------------------------------*/ +if(typeof Effect == 'undefined') + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + var Droppables = { drops: [], @@ -204,21 +207,31 @@ /*--------------------------------------------------------------------------*/ var Draggable = Class.create(); +Draggable._revertCache = {}; +Draggable._dragging = {}; + Draggable.prototype = { initialize: function(element) { var options = Object.extend({ handle: false, starteffect: function(element) { - element._opacity = Element.getOpacity(element); + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); }, reverteffect: function(element, top_offset, left_offset) { var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; - element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur}); + Draggable._revertCache[element] = + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); }, endeffect: function(element) { - var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0 - new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); + var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ Draggable._dragging[element] = false } + }); }, zindex: 1000, revert: false, @@ -264,6 +277,8 @@ }, initDrag: function(event) { + if(typeof Draggable._dragging[this.element] != undefined && + Draggable._dragging[this.element]) return; if(Event.isLeftClick(event)) { // abort on form elements, fixes a Firefox issue var src = Event.element(event); @@ -274,9 +289,9 @@ src.tagName=='BUTTON' || src.tagName=='TEXTAREA')) return; - if(this.element._revert) { - this.element._revert.cancel(); - this.element._revert = null; + if(Draggable._revertCache[this.element]) { + Draggable._revertCache[this.element].cancel(); + Draggable._revertCache[this.element] = null; } var pointer = [Event.pointerX(event), Event.pointerY(event)]; @@ -442,6 +457,7 @@ }, startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; this.lastScrolled = new Date(); this.scrollInterval = setInterval(this.scroll.bind(this), 10); @@ -708,7 +724,7 @@ if(!Element.isParent(dropon, element)) { var index; - var children = Sortable.findElements(dropon, {tag: droponOptions.tag}); + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); var child = null; if(children) { @@ -869,7 +885,7 @@ if (options.tree) { return Sortable.tree(element, arguments[1]).children.map( function (item) { - return [name + Sortable._constructIndex(item) + "=" + + return [name + Sortable._constructIndex(item) + "[id]=" + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); }).flatten().join('&'); } else { Index: openacs-4/packages/ajaxhelper/www/scriptaculous/effects.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/scriptaculous/Attic/effects.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/ajaxhelper/www/scriptaculous/effects.js 20 Apr 2006 12:31:28 -0000 1.3 +++ openacs-4/packages/ajaxhelper/www/scriptaculous/effects.js 20 Aug 2006 14:39:23 -0000 1.4 @@ -105,6 +105,9 @@ var Effect = { tagifyText: function(element) { + if(typeof Builder == 'undefined') + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); + var tagifyStyle = 'position:relative'; if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1'; element = $(element); @@ -161,9 +164,8 @@ Effect.Transitions = {} -Effect.Transitions.linear = function(pos) { - return pos; -} +Effect.Transitions.linear = Prototype.K; + Effect.Transitions.sinoidal = function(pos) { return (-Math.cos(pos*Math.PI)/2) + 0.5; } @@ -353,7 +355,7 @@ initialize: function(element) { this.element = $(element); // make this work on IE on elements without 'layout' - if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + if(/MSIE/.test(navigator.userAgent) && (!this.element.currentStyle.hasLayout)) this.element.setStyle({zoom: 1}); var options = Object.extend({ from: this.element.getOpacity() || 0.0, @@ -393,8 +395,8 @@ }, update: function(position) { this.element.setStyle({ - left: this.options.x * position + this.originalLeft + 'px', - top: this.options.y * position + this.originalTop + 'px' + left: Math.round(this.options.x * position + this.originalLeft) + 'px', + top: Math.round(this.options.y * position + this.originalTop) + 'px' }); } }); @@ -433,7 +435,7 @@ this.originalLeft = this.element.offsetLeft; var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%'].each( function(fontSizeType) { + ['em','px','%','pt'].each( function(fontSizeType) { if(fontSize.indexOf(fontSizeType)>0) { this.fontSize = parseFloat(fontSize); this.fontSizeType = fontSizeType; @@ -462,8 +464,8 @@ }, setDimensions: function(height, width) { var d = {}; - if(this.options.scaleX) d.width = width + 'px'; - if(this.options.scaleY) d.height = height + 'px'; + if(this.options.scaleX) d.width = Math.round(width) + 'px'; + if(this.options.scaleY) d.height = Math.round(height) + 'px'; if(this.options.scaleFromCenter) { var topd = (height - this.dims[0])/2; var leftd = (width - this.dims[1])/2; @@ -589,7 +591,7 @@ Effect.BlindUp = function(element) { element = $(element); element.makeClipping(); - return new Effect.Scale(element, 0, + return new Effect.Scale(element, 0, Object.extend({ scaleContent: false, scaleX: false, restoreAfterFinish: true, @@ -604,28 +606,27 @@ Effect.BlindDown = function(element) { element = $(element); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, - Object.extend({ scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping(); - effect.element.setStyle({height: '0px'}); - effect.element.show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || {}) - ); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping(); + effect.element.setStyle({height: '0px'}); + effect.element.show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {})); } Effect.SwitchOff = function(element) { element = $(element); var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, { + return new Effect.Appear(element, Object.extend({ duration: 0.4, from: 0, transition: Effect.Transitions.flicker, @@ -645,7 +646,7 @@ } }) } - }); + }, arguments[1] || {})); } Effect.DropOut = function(element) { @@ -729,7 +730,7 @@ }, arguments[1] || {}) ); } - + Effect.SlideUp = function(element) { element = $(element); element.cleanWhitespace(); Index: openacs-4/packages/ajaxhelper/www/scriptaculous/scriptaculous.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/scriptaculous/Attic/scriptaculous.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/ajaxhelper/www/scriptaculous/scriptaculous.js 20 Apr 2006 12:31:28 -0000 1.3 +++ openacs-4/packages/ajaxhelper/www/scriptaculous/scriptaculous.js 20 Aug 2006 14:39:23 -0000 1.4 @@ -20,7 +20,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var Scriptaculous = { - Version: '1.6.1', + Version: '1.6.2', require: function(libraryName) { // inserting via DOM fails in Safari 2.0, so brute force approach document.write(''); Index: openacs-4/packages/ajaxhelper/www/scriptaculous/slider.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/scriptaculous/Attic/slider.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/ajaxhelper/www/scriptaculous/slider.js 31 Mar 2006 08:15:36 -0000 1.2 +++ openacs-4/packages/ajaxhelper/www/scriptaculous/slider.js 20 Aug 2006 14:39:23 -0000 1.3 @@ -64,8 +64,13 @@ this.alignY = parseInt(this.options.alignY || '0'); this.trackLength = this.maximumOffset() - this.minimumOffset(); - this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth; + this.handleLength = this.isVertical() ? + (this.handles[0].offsetHeight != 0 ? + this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : + (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : + this.handles[0].style.width.replace(/px$/,"")); + this.active = false; this.dragging = false; this.disabled = false; @@ -137,8 +142,8 @@ }, setValue: function(sliderValue, handleIdx){ if(!this.active) { - this.activeHandle = this.handles[handleIdx]; - this.activeHandleIdx = handleIdx; + this.activeHandleIdx = handleIdx || 0; + this.activeHandle = this.handles[this.activeHandleIdx]; this.updateStyles(); } handleIdx = handleIdx || this.activeHandleIdx || 0; @@ -180,8 +185,11 @@ return(this.isVertical() ? this.alignY : this.alignX); }, maximumOffset: function(){ - return(this.isVertical() ? - this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX); + return(this.isVertical() ? + (this.track.offsetHeight != 0 ? this.track.offsetHeight : + this.track.style.height.replace(/px$/,"")) - this.alignY : + (this.track.offsetWidth != 0 ? this.track.offsetWidth : + this.track.style.width.replace(/px$/,"")) - this.alignY); }, isVertical: function(){ return (this.axis == 'vertical'); @@ -217,7 +225,8 @@ var handle = Event.element(event); var pointer = [Event.pointerX(event), Event.pointerY(event)]; - if(handle==this.track) { + var track = handle; + if(track==this.track) { var offsets = Position.cumulativeOffset(this.track); this.event = event; this.setValue(this.translateToValue( Index: openacs-4/packages/ajaxhelper/www/scriptaculous/unittest.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/scriptaculous/Attic/unittest.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/ajaxhelper/www/scriptaculous/unittest.js 20 Apr 2006 12:31:28 -0000 1.3 +++ openacs-4/packages/ajaxhelper/www/scriptaculous/unittest.js 20 Aug 2006 14:39:23 -0000 1.4 @@ -303,6 +303,13 @@ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } catch(e) { this.error(e); } }, + assertMatch: function(expected, actual) { + var message = arguments[2] || 'assertMatch'; + var regex = new RegExp(expected); + try { (regex.exec(actual)) ? this.pass() : + this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, assertHidden: function(element) { var message = arguments[1] || 'assertHidden'; this.assertEqual("none", element.style.display, message); Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/YAHOO.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/license.txt'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/connection/license.txt'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/connection/build/YAHOO.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/connection/build/connection.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dom/license.txt'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dom/build/YAHOO.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dom/build/dom.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dragdrop/license.txt'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dragdrop/build/YAHOO.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dragdrop/build/dragdrop-debug.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/dragdrop/build/dragdrop.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/event/license.txt'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/event/build/YAHOO.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/ajaxhelper/www/yahoo/event/build/event.js'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/ajaxhelper/www/yui/animation/README =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/animation/Attic/README,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/animation/README 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1,24 @@ +Animation Release Notes + +*** version 0.11.1 *** + +* changed "prototype" shorthand to "proto" (workaround firefox < 1.5 scoping bug) + +*** version 0.11.0 *** + +* ColorAnim subclass added +* Motion and Scroll now inherit from ColorAnim +* getDefaultUnit method added +* defaultUnit and defaultUnits deprecated +* getDefault and setDefault methods deprecated + +*** version 0.10.0 *** + +* Scroll now handles relative ("by") animation correctly + +* Now converts "auto" values of "from" to appropriate initial values + +*** version 0.9.0 *** + +* Initial release + Index: openacs-4/packages/ajaxhelper/www/yui/animation/animation-debug.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/animation/Attic/animation-debug.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/animation/animation-debug.js 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1,1261 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +Version: 0.11.1 +*/ + + +/** + * + * Base class for animated DOM objects. + * @class Base animation class that provides the interface for building animated effects. + *

Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + +YAHOO.util.Anim = function(el, attributes, duration, method) { + if (el) { + this.init(el, attributes, duration, method); + } +}; + +YAHOO.util.Anim.prototype = { + /** + * toString method + * @return {String} string represenation of anim obj + */ + toString: function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Anim " + id); + }, + + patterns: { // cached for performance + noNegatives: /width|height|opacity|padding/i, // keep at zero or above + offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default + defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default + offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset + }, + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + doMethod: function(attr, start, end) { + return this.method(this.currentFrame, start, end - start, this.totalFrames); + }, + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + setAttribute: function(attr, val, unit) { + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + YAHOO.util.Dom.setStyle(this.getEl(), attr, val + unit); + }, + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + getAttribute: function(attr) { + var el = this.getEl(); + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { + return parseFloat(val); + } + + var a = this.patterns.offsetAttribute.exec(attr) || []; + var pos = !!( a[3] ); // top or left + var box = !!( a[2] ); // width or height + + // use offsets for width/height and abs pos top/left + if ( box || (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute' && pos) ) { + val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; + } else { // default to zero for other 'auto' + val = 0; + } + + return val; + }, + + /** + * Returns the unit to use when none is supplied. + * Applies the "defaultUnit" test to decide whether to use pixels or not + * @param {attr} attr The name of the attribute. + * @return {String} The default unit to be used. + */ + getDefaultUnit: function(attr) { + if ( this.patterns.defaultUnit.test(attr) ) { + return 'px'; + } + + return ''; + }, + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + setRuntimeAttribute: function(attr) { + var start; + var end; + var attributes = this.attributes; + + this.runtimeAttributes[attr] = {}; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; + + if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { + return false; // note return; nothing to animate to + } + + start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); + + // To beats by, per SMIL 2.1 spec + if ( isset(attributes[attr]['to']) ) { + end = attributes[attr]['to']; + } else if ( isset(attributes[attr]['by']) ) { + if (start.constructor == Array) { + end = []; + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + attributes[attr]['by'][i]; + } + } else { + end = start + attributes[attr]['by']; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + + // set units if needed + this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr); + }, + + /** + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + init: function(el, attributes, duration, method) { + /** + * Whether or not the animation is running. + * @private + * @type Boolean + */ + var isAnimated = false; + + /** + * A Date object that is created when the animation begins. + * @private + * @type Date + */ + var startTime = null; + + /** + * The number of frames this animation was able to execute. + * @private + * @type Int + */ + var actualFrames = 0; + + /** + * The element to be animated. + * @private + * @type HTMLElement + */ + el = YAHOO.util.Dom.get(el); + + /** + * The collection of attributes to be animated. + * Each attribute must have at least a "to" or "by" defined in order to animate. + * If "to" is supplied, the animation will end with the attribute at that value. + * If "by" is supplied, the animation will end at that value plus its starting value. + * If both are supplied, "to" is used, and "by" is ignored. + * @member YAHOO#util#Anim + * 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). + * @type Object + */ + this.attributes = attributes || {}; + + /** + * The length of the animation. Defaults to "1" (second). + * @type Number + */ + this.duration = duration || 1; + + /** + * The method that will provide values to the attribute(s) during the animation. + * Defaults to "YAHOO.util.Easing.easeNone". + * @type Function + */ + this.method = method || YAHOO.util.Easing.easeNone; + + /** + * Whether or not the duration should be treated as seconds. + * Defaults to true. + * @type Boolean + */ + this.useSeconds = true; // default to seconds + + /** + * The location of the current animation on the timeline. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @type Int + */ + this.currentFrame = 0; + + /** + * The total number of frames to be executed. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @type Int + */ + this.totalFrames = YAHOO.util.AnimMgr.fps; + + + /** + * Returns a reference to the animated element. + * @return {HTMLElement} + */ + this.getEl = function() { return el; }; + + /** + * Checks whether the element is currently animated. + * @return {Boolean} current value of isAnimated. + */ + this.isAnimated = function() { + return isAnimated; + }; + + /** + * Returns the animation start time. + * @return {Date} current value of startTime. + */ + this.getStartTime = function() { + return startTime; + }; + + this.runtimeAttributes = {}; + + var logger = {}; + logger.log = function() {YAHOO.log.apply(window, arguments)}; + + logger.log('creating new instance of ' + this); + + /** + * Starts the animation by registering it with the animation manager. + */ + this.animate = function() { + if ( this.isAnimated() ) { return false; } + + this.currentFrame = 0; + + this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration; + + YAHOO.util.AnimMgr.registerElement(this); + }; + + /** + * Stops the animation. Normally called by AnimMgr when animation completes. + */ + this.stop = function() { + YAHOO.util.AnimMgr.stop(this); + }; + + var onStart = function() { + this.onStart.fire(); + for (var attr in this.attributes) { + this.setRuntimeAttribute(attr); + } + + isAnimated = true; + actualFrames = 0; + startTime = new Date(); + }; + + /** + * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s). + * @private + */ + + var onTween = function() { + var data = { + duration: new Date() - this.getStartTime(), + currentFrame: this.currentFrame + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', currentFrame: ' + data.currentFrame + ); + }; + + this.onTween.fire(data); + + var runtimeAttributes = this.runtimeAttributes; + + for (var attr in runtimeAttributes) { + this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); + } + + actualFrames += 1; + }; + + var onComplete = function() { + var actual_duration = (new Date() - startTime) / 1000 ; + + var data = { + duration: actual_duration, + frames: actualFrames, + fps: actualFrames / actual_duration + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', frames: ' + data.frames + + ', fps: ' + data.fps + ); + }; + + isAnimated = false; + actualFrames = 0; + this.onComplete.fire(data); + }; + + /** + * Custom event that fires after onStart, useful in subclassing + * @private + */ + this._onStart = new YAHOO.util.CustomEvent('_start', this, true); + + /** + * Custom event that fires when animation begins + * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction) + */ + this.onStart = new YAHOO.util.CustomEvent('start', this); + + /** + * Custom event that fires between each frame + * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction) + */ + this.onTween = new YAHOO.util.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new YAHOO.util.CustomEvent('_tween', this, true); + + /** + * Custom event that fires when animation ends + * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction) + */ + this.onComplete = new YAHOO.util.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + +/** + * @class Handles animation queueing and threading. + * Used by Anim and subclasses. + */ +YAHOO.util.AnimMgr = new function() { + /** + * Reference to the animation Interval + * @private + * @type Int + */ + var thread = null; + + /** + * The current queue of registered animation objects. + * @private + * @type Array + */ + var queue = []; + + /** + * The number of active animations. + * @private + * @type Int + */ + var tweenCount = 0; + + /** + * Base frame rate (frames per second). + * Arbitrarily high for better x-browser calibration (slower browsers drop more frames). + * @type Int + * + */ + this.fps = 200; + + /** + * Interval delay in milliseconds, defaults to fastest possible. + * @type Int + * + */ + this.delay = 1; + + /** + * Adds an animation instance to the animation queue. + * All animation instances must be registered in order to animate. + * @param {object} tween The Anim instance to be be registered + */ + this.registerElement = function(tween) { + queue[queue.length] = tween; + tweenCount += 1; + tween._onStart.fire(); + this.start(); + }; + + this.unRegister = function(tween, index) { + tween._onComplete.fire(); + index = index || getIndex(tween); + if (index != -1) { queue.splice(index, 1); } + + tweenCount -= 1; + if (tweenCount <= 0) { this.stop(); } + }; + + /** + * Starts the animation thread. + * Only one thread can run at a time. + */ + this.start = function() { + if (thread === null) { thread = setInterval(this.run, this.delay); } + }; + + /** + * Stops the animation thread or a specific animation instance. + * @param {object} tween A specific Anim instance to stop (optional) + * If no instance given, Manager stops thread and all animations. + */ + this.stop = function(tween) { + if (!tween) { + clearInterval(thread); + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i].isAnimated()) { + this.unRegister(tween, i); + } + } + queue = []; + thread = null; + tweenCount = 0; + } + else { + this.unRegister(tween); + } + }; + + /** + * Called per Interval to handle each animation frame. + */ + this.run = function() { + for (var i = 0, len = queue.length; i < len; ++i) { + var tween = queue[i]; + if ( !tween || !tween.isAnimated() ) { continue; } + + if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) + { + tween.currentFrame += 1; + + if (tween.useSeconds) { + correctFrame(tween); + } + tween._onTween.fire(); + } + else { YAHOO.util.AnimMgr.stop(tween, i); } + } + }; + + var getIndex = function(anim) { + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i] == anim) { + return i; // note return; + } + } + return -1; + }; + + /** + * On the fly frame correction to keep animation on time. + * @private + * @param {Object} tween The Anim instance being corrected. + */ + var correctFrame = function(tween) { + var frames = tween.totalFrames; + var frame = tween.currentFrame; + var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); + var elapsed = (new Date() - tween.getStartTime()); + var tweak = 0; + + if (elapsed < tween.duration * 1000) { // check if falling behind + tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); + } else { // went over duration, so jump to end + tweak = frames - (frame + 1); + } + if (tweak > 0 && isFinite(tweak)) { // adjust if needed + if (tween.currentFrame + tweak >= frames) {// dont go past last frame + tweak = frames - (frame + 1); + } + + tween.currentFrame += tweak; + } + }; +}; +/** + * + * @class Used to calculate Bezier splines for any number of control points. + * + */ +YAHOO.util.Bezier = new function() +{ + /** + * Get the current position of the animated element based on t. + * Each point is an array of "x" and "y" values (0 = x, 1 = y) + * At least 2 points are required (start and end). + * First point is start. Last point is end. + * Additional control points are optional. + * @param {Array} points An array containing Bezier points + * @param {Number} t A number between 0 and 1 which is the basis for determining current position + * @return {Array} An array containing int x and y member data + */ + this.getPosition = function(points, t) + { + var n = points.length; + var tmp = []; + + for (var i = 0; i < n; ++i){ + tmp[i] = [points[i][0], points[i][1]]; // save input + } + + for (var j = 1; j < n; ++j) { + for (i = 0; i < n - j; ++i) { + tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; + tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; + } + } + + return [ tmp[0][0], tmp[0][1] ]; + + }; +}; +/** + * @class ColorAnim subclass for color fading + *

Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);

+ *

Color values can be specified with either 112233, #112233, [255,255,255], or rgb(255,255,255) + * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @constructor + * @param {HTMLElement | String} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.ColorAnim = function(el, attributes, duration, method) { + YAHOO.util.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method); + }; + + YAHOO.extend(YAHOO.util.ColorAnim, YAHOO.util.Anim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.ColorAnim.superclass; + var proto = Y.ColorAnim.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("ColorAnim " + id); + }; + + /** + * Only certain attributes should be treated as colors. + * @type Object + */ + proto.patterns.color = /color$/i; + proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i; + proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i; + proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i; + + /** + * Attempts to parse the given string and return a 3-tuple. + * @param {String} s The string to parse. + * @return {Array} The 3-tuple of rgb values. + */ + proto.parseColor = function(s) { + if (s.length == 3) { return s; } + + var c = this.patterns.hex.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ]; + } + + c = this.patterns.rgb.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ]; + } + + c = this.patterns.hex3.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ]; + } + + return null; + }; + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + proto.getAttribute = function(attr) { + var el = this.getEl(); + if ( this.patterns.color.test(attr) ) { + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val == 'transparent') { // bgcolor default + var parent = el.parentNode; // try and get from an ancestor + val = Y.Dom.getStyle(parent, attr); + + while (parent && val == 'transparent') { + parent = parent.parentNode; + val = Y.Dom.getStyle(parent, attr); + if (parent.tagName.toUpperCase() == 'HTML') { + val = 'ffffff'; + } + } + } + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val; + + if ( this.patterns.color.test(attr) ) { + val = []; + for (var i = 0, len = start.length; i < len; ++i) { + val[i] = superclass.doMethod.call(this, attr, start[i], end[i]); + } + + val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')'; + } + else { + val = superclass.doMethod.call(this, attr, start, end); + } + + return val; + }; + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + proto.setRuntimeAttribute = function(attr) { + superclass.setRuntimeAttribute.call(this, attr); + + if ( this.patterns.color.test(attr) ) { + var attributes = this.attributes; + var start = this.parseColor(this.runtimeAttributes[attr].start); + var end = this.parseColor(this.runtimeAttributes[attr].end); + // fix colors if going "by" + if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) { + end = this.parseColor(attributes[attr].by); + + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + end[i]; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + } + }; +})();/* +TERMS OF USE - EASING EQUATIONS +Open source under the BSD License. +Copyright � 2001 Robert Penner All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * 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. + +THIS 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. + +*/ + +YAHOO.util.Easing = { + + /** + * Uniform speed between points. + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeNone: function (t, b, c, d) { + return c*t/d + b; + }, + + /** + * Begins slowly and accelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeIn: function (t, b, c, d) { + return c*(t/=d)*t + b; + }, + + /** + * Begins quickly and decelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeOut: function (t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + + /** + * Begins slowly and decelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeBoth: function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + + /** + * Begins slowly and accelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeInStrong: function (t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + + /** + * Begins quickly and decelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeOutStrong: function (t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + + /** + * Begins slowly and decelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeBothStrong: function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + + /** + * snap in elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + + elasticIn: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + + /** + * snap out elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + elasticOut: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + + /** + * snap both elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + elasticBoth: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + + + /** + * back easing in - backtracking slightly, then reversing direction and moving to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backIn: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + + /** + * back easing out - moving towards target, overshooting it slightly, + * then reversing and coming back to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backOut: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + + /** + * back easing in/out - backtracking slightly, then reversing direction and moving to target, + * then overshooting target, reversing, and finally coming back to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backBoth: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + + /** + * bounce in + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceIn: function (t, b, c, d) { + return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b; + }, + + /** + * bounce out + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceOut: function (t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + + /** + * bounce both + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceBoth: function (t, b, c, d) { + if (t < d/2) return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b; + return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}; + +/** + * @class Anim subclass for moving elements along a path defined by the "points" member of "attributes". All "points" are arrays with x, y coordinates. + *

Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.Motion = function(el, attributes, duration, method) { + if (el) { // dont break existing subclasses not using YAHOO.extend + YAHOO.util.Motion.superclass.constructor.call(this, el, attributes, duration, method); + } + }; + + YAHOO.extend(YAHOO.util.Motion, YAHOO.util.ColorAnim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.Motion.superclass; + var proto = Y.Motion.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Motion " + id); + }; + + proto.patterns.points = /^points$/i; + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + proto.setAttribute = function(attr, val, unit) { + if ( this.patterns.points.test(attr) ) { + unit = unit || 'px'; + superclass.setAttribute.call(this, 'left', val[0], unit); + superclass.setAttribute.call(this, 'top', val[1], unit); + } else { + superclass.setAttribute.call(this, attr, val, unit); + } + }; + + /** + * Sets the default value to be used when "from" is not supplied. + * @param {String} attr The attribute being set. + * @param {Number} val The default value to be applied to the attribute. + */ + proto.getAttribute = function(attr) { + if ( this.patterns.points.test(attr) ) { + var val = [ + superclass.getAttribute.call(this, 'left'), + superclass.getAttribute.call(this, 'top') + ]; + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val = null; + + if ( this.patterns.points.test(attr) ) { + var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100; + val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t); + } else { + val = superclass.doMethod.call(this, attr, start, end); + } + return val; + }; + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + proto.setRuntimeAttribute = function(attr) { + if ( this.patterns.points.test(attr) ) { + var el = this.getEl(); + var attributes = this.attributes; + var start; + var control = attributes['points']['control'] || []; + var end; + var i, len; + + if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points + control = [control]; + } else { // break reference to attributes.points.control + var tmp = []; + for (i = 0, len = control.length; i< len; ++i) { + tmp[i] = control[i]; + } + control = tmp; + } + + if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative + Y.Dom.setStyle(el, 'position', 'relative'); + } + + if ( isset(attributes['points']['from']) ) { + Y.Dom.setXY(el, attributes['points']['from']); // set position to from point + } + else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position + + start = this.getAttribute('points'); // get actual top & left + + // TO beats BY, per SMIL 2.1 spec + if ( isset(attributes['points']['to']) ) { + end = translateValues.call(this, attributes['points']['to'], start); + + var pageXY = Y.Dom.getXY(this.getEl()); + for (i = 0, len = control.length; i < len; ++i) { + control[i] = translateValues.call(this, control[i], start); + } + + + } else if ( isset(attributes['points']['by']) ) { + end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ]; + + for (i = 0, len = control.length; i < len; ++i) { + control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ]; + } + } + + this.runtimeAttributes[attr] = [start]; + + if (control.length > 0) { + this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control); + } + + this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end; + } + else { + superclass.setRuntimeAttribute.call(this, attr); + } + }; + + var translateValues = function(val, start) { + var pageXY = Y.Dom.getXY(this.getEl()); + val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ]; + + return val; + }; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; +})(); +/** + * @class Anim subclass for scrolling elements to a position defined by the "scroll" member of "attributes". All "scroll" members are arrays with x, y scroll positions. + *

Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.Scroll = function(el, attributes, duration, method) { + if (el) { // dont break existing subclasses not using YAHOO.extend + YAHOO.util.Scroll.superclass.constructor.call(this, el, attributes, duration, method); + } + }; + + YAHOO.extend(YAHOO.util.Scroll, YAHOO.util.ColorAnim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.Scroll.superclass; + var proto = Y.Scroll.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Scroll " + id); + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val = null; + + if (attr == 'scroll') { + val = [ + this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames), + this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames) + ]; + + } else { + val = superclass.doMethod.call(this, attr, start, end); + } + return val; + }; + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + proto.getAttribute = function(attr) { + var val = null; + var el = this.getEl(); + + if (attr == 'scroll') { + val = [ el.scrollLeft, el.scrollTop ]; + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + proto.setAttribute = function(attr, val, unit) { + var el = this.getEl(); + + if (attr == 'scroll') { + el.scrollLeft = val[0]; + el.scrollTop = val[1]; + } else { + superclass.setAttribute.call(this, attr, val, unit); + } + }; +})(); Index: openacs-4/packages/ajaxhelper/www/yui/animation/animation-min.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/animation/Attic/animation-min.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/animation/animation-min.js 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt Version: 0.11.1 */ YAHOO.util.Anim=function(el,attributes,duration,method){if(el){this.init(el,attributes,duration,method);}};YAHOO.util.Anim.prototype={toString:function(){var el=this.getEl();var id=el.id||el.tagName;return("Anim "+id);},patterns:{noNegatives:/width|height|opacity|padding/i,offsetAttribute:/^((width|height)|(top|left))$/,defaultUnit:/width|height|top$|bottom$|left$|right$/i,offsetUnit:/\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i},doMethod:function(attr,start,end){return this.method(this.currentFrame,start,end-start,this.totalFrames);},setAttribute:function(attr,val,unit){if(this.patterns.noNegatives.test(attr)){val=(val>0)?val:0;}YAHOO.util.Dom.setStyle(this.getEl(),attr,val+unit);},getAttribute:function(attr){var el=this.getEl();var val=YAHOO.util.Dom.getStyle(el,attr);if(val!=='auto'&&!this.patterns.offsetUnit.test(val)){return parseFloat(val);}var a=this.patterns.offsetAttribute.exec(attr)||[];var pos=!!(a[3]);var box=!!(a[2]);if(box||(YAHOO.util.Dom.getStyle(el,'position')=='absolute'&&pos)){val=el['offset'+a[0].charAt(0).toUpperCase()+a[0].substr(1)];}else{val=0;}return val;},getDefaultUnit:function(attr){if(this.patterns.defaultUnit.test(attr)){return'px';}return'';},setRuntimeAttribute:function(attr){var start;var end;var attributes=this.attributes;this.runtimeAttributes[attr]={};var isset=function(prop){return(typeof prop!=='undefined');};if(!isset(attributes[attr]['to'])&&!isset(attributes[attr]['by'])){return false;}start=(isset(attributes[attr]['from']))?attributes[attr]['from']:this.getAttribute(attr);if(isset(attributes[attr]['to'])){end=attributes[attr]['to'];}else if(isset(attributes[attr]['by'])){if(start.constructor==Array){end=[];for(var i=0,len=start.length;i0&&isFinite(tweak)){if(tween.currentFrame+tweak>=frames){tweak=frames-(frame+1);}tween.currentFrame+=tweak;}};};YAHOO.util.Bezier=new function(){this.getPosition=function(points,t){var n=points.length;var tmp=[];for(var i=0;i0&&!(control[0]instanceof Array)){control=[control];}else{var tmp=[];for(i=0,len=control.length;i0){this.runtimeAttributes[attr]=this.runtimeAttributes[attr].concat(control);}this.runtimeAttributes[attr][this.runtimeAttributes[attr].length]=end;}else{superclass.setRuntimeAttribute.call(this,attr);}};var translateValues=function(val,start){var pageXY=Y.Dom.getXY(this.getEl());val=[val[0]-pageXY[0]+start[0],val[1]-pageXY[1]+start[1]];return val;};var isset=function(prop){return(typeof prop!=='undefined');};})();(function(){YAHOO.util.Scroll=function(el,attributes,duration,method){if(el){YAHOO.util.Scroll.superclass.constructor.call(this,el,attributes,duration,method);}};YAHOO.extend(YAHOO.util.Scroll,YAHOO.util.ColorAnim);var Y=YAHOO.util;var superclass=Y.Scroll.superclass;var proto=Y.Scroll.prototype;proto.toString=function(){var el=this.getEl();var id=el.id||el.tagName;return("Scroll "+id);};proto.doMethod=function(attr,start,end){var val=null;if(attr=='scroll'){val=[this.method(this.currentFrame,start[0],end[0]-start[0],this.totalFrames),this.method(this.currentFrame,start[1],end[1]-start[1],this.totalFrames)];}else{val=superclass.doMethod.call(this,attr,start,end);}return val;};proto.getAttribute=function(attr){var val=null;var el=this.getEl();if(attr=='scroll'){val=[el.scrollLeft,el.scrollTop];}else{val=superclass.getAttribute.call(this,attr);}return val;};proto.setAttribute=function(attr,val,unit){var el=this.getEl();if(attr=='scroll'){el.scrollLeft=val[0];el.scrollTop=val[1];}else{superclass.setAttribute.call(this,attr,val,unit);}};})(); Index: openacs-4/packages/ajaxhelper/www/yui/animation/animation.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/animation/Attic/animation.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/animation/animation.js 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1,1255 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +Version: 0.11.1 +*/ +/** + * + * Base class for animated DOM objects. + * @class Base animation class that provides the interface for building animated effects. + *

Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + +YAHOO.util.Anim = function(el, attributes, duration, method) { + if (el) { + this.init(el, attributes, duration, method); + } +}; + +YAHOO.util.Anim.prototype = { + /** + * toString method + * @return {String} string represenation of anim obj + */ + toString: function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Anim " + id); + }, + + patterns: { // cached for performance + noNegatives: /width|height|opacity|padding/i, // keep at zero or above + offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default + defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default + offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset + }, + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + doMethod: function(attr, start, end) { + return this.method(this.currentFrame, start, end - start, this.totalFrames); + }, + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + setAttribute: function(attr, val, unit) { + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + YAHOO.util.Dom.setStyle(this.getEl(), attr, val + unit); + }, + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + getAttribute: function(attr) { + var el = this.getEl(); + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { + return parseFloat(val); + } + + var a = this.patterns.offsetAttribute.exec(attr) || []; + var pos = !!( a[3] ); // top or left + var box = !!( a[2] ); // width or height + + // use offsets for width/height and abs pos top/left + if ( box || (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute' && pos) ) { + val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; + } else { // default to zero for other 'auto' + val = 0; + } + + return val; + }, + + /** + * Returns the unit to use when none is supplied. + * Applies the "defaultUnit" test to decide whether to use pixels or not + * @param {attr} attr The name of the attribute. + * @return {String} The default unit to be used. + */ + getDefaultUnit: function(attr) { + if ( this.patterns.defaultUnit.test(attr) ) { + return 'px'; + } + + return ''; + }, + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + setRuntimeAttribute: function(attr) { + var start; + var end; + var attributes = this.attributes; + + this.runtimeAttributes[attr] = {}; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; + + if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { + return false; // note return; nothing to animate to + } + + start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); + + // To beats by, per SMIL 2.1 spec + if ( isset(attributes[attr]['to']) ) { + end = attributes[attr]['to']; + } else if ( isset(attributes[attr]['by']) ) { + if (start.constructor == Array) { + end = []; + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + attributes[attr]['by'][i]; + } + } else { + end = start + attributes[attr]['by']; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + + // set units if needed + this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr); + }, + + /** + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + init: function(el, attributes, duration, method) { + /** + * Whether or not the animation is running. + * @private + * @type Boolean + */ + var isAnimated = false; + + /** + * A Date object that is created when the animation begins. + * @private + * @type Date + */ + var startTime = null; + + /** + * The number of frames this animation was able to execute. + * @private + * @type Int + */ + var actualFrames = 0; + + /** + * The element to be animated. + * @private + * @type HTMLElement + */ + el = YAHOO.util.Dom.get(el); + + /** + * The collection of attributes to be animated. + * Each attribute must have at least a "to" or "by" defined in order to animate. + * If "to" is supplied, the animation will end with the attribute at that value. + * If "by" is supplied, the animation will end at that value plus its starting value. + * If both are supplied, "to" is used, and "by" is ignored. + * @member YAHOO#util#Anim + * 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). + * @type Object + */ + this.attributes = attributes || {}; + + /** + * The length of the animation. Defaults to "1" (second). + * @type Number + */ + this.duration = duration || 1; + + /** + * The method that will provide values to the attribute(s) during the animation. + * Defaults to "YAHOO.util.Easing.easeNone". + * @type Function + */ + this.method = method || YAHOO.util.Easing.easeNone; + + /** + * Whether or not the duration should be treated as seconds. + * Defaults to true. + * @type Boolean + */ + this.useSeconds = true; // default to seconds + + /** + * The location of the current animation on the timeline. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @type Int + */ + this.currentFrame = 0; + + /** + * The total number of frames to be executed. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @type Int + */ + this.totalFrames = YAHOO.util.AnimMgr.fps; + + + /** + * Returns a reference to the animated element. + * @return {HTMLElement} + */ + this.getEl = function() { return el; }; + + /** + * Checks whether the element is currently animated. + * @return {Boolean} current value of isAnimated. + */ + this.isAnimated = function() { + return isAnimated; + }; + + /** + * Returns the animation start time. + * @return {Date} current value of startTime. + */ + this.getStartTime = function() { + return startTime; + }; + + this.runtimeAttributes = {}; + + + + /** + * Starts the animation by registering it with the animation manager. + */ + this.animate = function() { + if ( this.isAnimated() ) { return false; } + + this.currentFrame = 0; + + this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration; + + YAHOO.util.AnimMgr.registerElement(this); + }; + + /** + * Stops the animation. Normally called by AnimMgr when animation completes. + */ + this.stop = function() { + YAHOO.util.AnimMgr.stop(this); + }; + + var onStart = function() { + this.onStart.fire(); + for (var attr in this.attributes) { + this.setRuntimeAttribute(attr); + } + + isAnimated = true; + actualFrames = 0; + startTime = new Date(); + }; + + /** + * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s). + * @private + */ + + var onTween = function() { + var data = { + duration: new Date() - this.getStartTime(), + currentFrame: this.currentFrame + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', currentFrame: ' + data.currentFrame + ); + }; + + this.onTween.fire(data); + + var runtimeAttributes = this.runtimeAttributes; + + for (var attr in runtimeAttributes) { + this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); + } + + actualFrames += 1; + }; + + var onComplete = function() { + var actual_duration = (new Date() - startTime) / 1000 ; + + var data = { + duration: actual_duration, + frames: actualFrames, + fps: actualFrames / actual_duration + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', frames: ' + data.frames + + ', fps: ' + data.fps + ); + }; + + isAnimated = false; + actualFrames = 0; + this.onComplete.fire(data); + }; + + /** + * Custom event that fires after onStart, useful in subclassing + * @private + */ + this._onStart = new YAHOO.util.CustomEvent('_start', this, true); + + /** + * Custom event that fires when animation begins + * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction) + */ + this.onStart = new YAHOO.util.CustomEvent('start', this); + + /** + * Custom event that fires between each frame + * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction) + */ + this.onTween = new YAHOO.util.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new YAHOO.util.CustomEvent('_tween', this, true); + + /** + * Custom event that fires when animation ends + * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction) + */ + this.onComplete = new YAHOO.util.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + +/** + * @class Handles animation queueing and threading. + * Used by Anim and subclasses. + */ +YAHOO.util.AnimMgr = new function() { + /** + * Reference to the animation Interval + * @private + * @type Int + */ + var thread = null; + + /** + * The current queue of registered animation objects. + * @private + * @type Array + */ + var queue = []; + + /** + * The number of active animations. + * @private + * @type Int + */ + var tweenCount = 0; + + /** + * Base frame rate (frames per second). + * Arbitrarily high for better x-browser calibration (slower browsers drop more frames). + * @type Int + * + */ + this.fps = 200; + + /** + * Interval delay in milliseconds, defaults to fastest possible. + * @type Int + * + */ + this.delay = 1; + + /** + * Adds an animation instance to the animation queue. + * All animation instances must be registered in order to animate. + * @param {object} tween The Anim instance to be be registered + */ + this.registerElement = function(tween) { + queue[queue.length] = tween; + tweenCount += 1; + tween._onStart.fire(); + this.start(); + }; + + this.unRegister = function(tween, index) { + tween._onComplete.fire(); + index = index || getIndex(tween); + if (index != -1) { queue.splice(index, 1); } + + tweenCount -= 1; + if (tweenCount <= 0) { this.stop(); } + }; + + /** + * Starts the animation thread. + * Only one thread can run at a time. + */ + this.start = function() { + if (thread === null) { thread = setInterval(this.run, this.delay); } + }; + + /** + * Stops the animation thread or a specific animation instance. + * @param {object} tween A specific Anim instance to stop (optional) + * If no instance given, Manager stops thread and all animations. + */ + this.stop = function(tween) { + if (!tween) { + clearInterval(thread); + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i].isAnimated()) { + this.unRegister(tween, i); + } + } + queue = []; + thread = null; + tweenCount = 0; + } + else { + this.unRegister(tween); + } + }; + + /** + * Called per Interval to handle each animation frame. + */ + this.run = function() { + for (var i = 0, len = queue.length; i < len; ++i) { + var tween = queue[i]; + if ( !tween || !tween.isAnimated() ) { continue; } + + if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) + { + tween.currentFrame += 1; + + if (tween.useSeconds) { + correctFrame(tween); + } + tween._onTween.fire(); + } + else { YAHOO.util.AnimMgr.stop(tween, i); } + } + }; + + var getIndex = function(anim) { + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i] == anim) { + return i; // note return; + } + } + return -1; + }; + + /** + * On the fly frame correction to keep animation on time. + * @private + * @param {Object} tween The Anim instance being corrected. + */ + var correctFrame = function(tween) { + var frames = tween.totalFrames; + var frame = tween.currentFrame; + var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); + var elapsed = (new Date() - tween.getStartTime()); + var tweak = 0; + + if (elapsed < tween.duration * 1000) { // check if falling behind + tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); + } else { // went over duration, so jump to end + tweak = frames - (frame + 1); + } + if (tweak > 0 && isFinite(tweak)) { // adjust if needed + if (tween.currentFrame + tweak >= frames) {// dont go past last frame + tweak = frames - (frame + 1); + } + + tween.currentFrame += tweak; + } + }; +}; +/** + * + * @class Used to calculate Bezier splines for any number of control points. + * + */ +YAHOO.util.Bezier = new function() +{ + /** + * Get the current position of the animated element based on t. + * Each point is an array of "x" and "y" values (0 = x, 1 = y) + * At least 2 points are required (start and end). + * First point is start. Last point is end. + * Additional control points are optional. + * @param {Array} points An array containing Bezier points + * @param {Number} t A number between 0 and 1 which is the basis for determining current position + * @return {Array} An array containing int x and y member data + */ + this.getPosition = function(points, t) + { + var n = points.length; + var tmp = []; + + for (var i = 0; i < n; ++i){ + tmp[i] = [points[i][0], points[i][1]]; // save input + } + + for (var j = 1; j < n; ++j) { + for (i = 0; i < n - j; ++i) { + tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; + tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; + } + } + + return [ tmp[0][0], tmp[0][1] ]; + + }; +}; +/** + * @class ColorAnim subclass for color fading + *

Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);

+ *

Color values can be specified with either 112233, #112233, [255,255,255], or rgb(255,255,255) + * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @constructor + * @param {HTMLElement | String} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.ColorAnim = function(el, attributes, duration, method) { + YAHOO.util.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method); + }; + + YAHOO.extend(YAHOO.util.ColorAnim, YAHOO.util.Anim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.ColorAnim.superclass; + var proto = Y.ColorAnim.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("ColorAnim " + id); + }; + + /** + * Only certain attributes should be treated as colors. + * @type Object + */ + proto.patterns.color = /color$/i; + proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i; + proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i; + proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i; + + /** + * Attempts to parse the given string and return a 3-tuple. + * @param {String} s The string to parse. + * @return {Array} The 3-tuple of rgb values. + */ + proto.parseColor = function(s) { + if (s.length == 3) { return s; } + + var c = this.patterns.hex.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ]; + } + + c = this.patterns.rgb.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ]; + } + + c = this.patterns.hex3.exec(s); + if (c && c.length == 4) { + return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ]; + } + + return null; + }; + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + proto.getAttribute = function(attr) { + var el = this.getEl(); + if ( this.patterns.color.test(attr) ) { + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val == 'transparent') { // bgcolor default + var parent = el.parentNode; // try and get from an ancestor + val = Y.Dom.getStyle(parent, attr); + + while (parent && val == 'transparent') { + parent = parent.parentNode; + val = Y.Dom.getStyle(parent, attr); + if (parent.tagName.toUpperCase() == 'HTML') { + val = 'ffffff'; + } + } + } + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val; + + if ( this.patterns.color.test(attr) ) { + val = []; + for (var i = 0, len = start.length; i < len; ++i) { + val[i] = superclass.doMethod.call(this, attr, start[i], end[i]); + } + + val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')'; + } + else { + val = superclass.doMethod.call(this, attr, start, end); + } + + return val; + }; + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + proto.setRuntimeAttribute = function(attr) { + superclass.setRuntimeAttribute.call(this, attr); + + if ( this.patterns.color.test(attr) ) { + var attributes = this.attributes; + var start = this.parseColor(this.runtimeAttributes[attr].start); + var end = this.parseColor(this.runtimeAttributes[attr].end); + // fix colors if going "by" + if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) { + end = this.parseColor(attributes[attr].by); + + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + end[i]; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + } + }; +})();/* +TERMS OF USE - EASING EQUATIONS +Open source under the BSD License. +Copyright � 2001 Robert Penner All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * 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. + +THIS 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. + +*/ + +YAHOO.util.Easing = { + + /** + * Uniform speed between points. + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeNone: function (t, b, c, d) { + return c*t/d + b; + }, + + /** + * Begins slowly and accelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeIn: function (t, b, c, d) { + return c*(t/=d)*t + b; + }, + + /** + * Begins quickly and decelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeOut: function (t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + + /** + * Begins slowly and decelerates towards end. (quadratic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeBoth: function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + + /** + * Begins slowly and accelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeInStrong: function (t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + + /** + * Begins quickly and decelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeOutStrong: function (t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + + /** + * Begins slowly and decelerates towards end. (quartic) + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + easeBothStrong: function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + + /** + * snap in elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + + elasticIn: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + + /** + * snap out elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + elasticOut: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + + /** + * snap both elastic effect + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number} p Period (optional) + * @return {Number} The computed value for the current animation frame. + */ + elasticBoth: function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (!a || a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + + /** + * back easing in - backtracking slightly, then reversing direction and moving to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backIn: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + + /** + * back easing out - moving towards target, overshooting it slightly, + * then reversing and coming back to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backOut: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + + /** + * back easing in/out - backtracking slightly, then reversing direction and moving to target, + * then overshooting target, reversing, and finally coming back to target + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @param {Number) s Overshoot (optional) + * @return {Number} The computed value for the current animation frame. + */ + backBoth: function (t, b, c, d, s) { + if (typeof s == 'undefined') s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + + /** + * bounce in + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceIn: function (t, b, c, d) { + return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b; + }, + + /** + * bounce out + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceOut: function (t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + + /** + * bounce both + * @param {Number} t Time value used to compute current value. + * @param {Number} b Starting value. + * @param {Number} c Delta between start and end values. + * @param {Number} d Total length of animation. + * @return {Number} The computed value for the current animation frame. + */ + bounceBoth: function (t, b, c, d) { + if (t < d/2) return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b; + return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}; + +/** + * @class Anim subclass for moving elements along a path defined by the "points" member of "attributes". All "points" are arrays with x, y coordinates. + *

Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.Motion = function(el, attributes, duration, method) { + if (el) { // dont break existing subclasses not using YAHOO.extend + YAHOO.util.Motion.superclass.constructor.call(this, el, attributes, duration, method); + } + }; + + YAHOO.extend(YAHOO.util.Motion, YAHOO.util.ColorAnim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.Motion.superclass; + var proto = Y.Motion.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Motion " + id); + }; + + proto.patterns.points = /^points$/i; + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + proto.setAttribute = function(attr, val, unit) { + if ( this.patterns.points.test(attr) ) { + unit = unit || 'px'; + superclass.setAttribute.call(this, 'left', val[0], unit); + superclass.setAttribute.call(this, 'top', val[1], unit); + } else { + superclass.setAttribute.call(this, attr, val, unit); + } + }; + + /** + * Sets the default value to be used when "from" is not supplied. + * @param {String} attr The attribute being set. + * @param {Number} val The default value to be applied to the attribute. + */ + proto.getAttribute = function(attr) { + if ( this.patterns.points.test(attr) ) { + var val = [ + superclass.getAttribute.call(this, 'left'), + superclass.getAttribute.call(this, 'top') + ]; + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val = null; + + if ( this.patterns.points.test(attr) ) { + var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100; + val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t); + } else { + val = superclass.doMethod.call(this, attr, start, end); + } + return val; + }; + + /** + * Sets the actual values to be used during the animation. + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + proto.setRuntimeAttribute = function(attr) { + if ( this.patterns.points.test(attr) ) { + var el = this.getEl(); + var attributes = this.attributes; + var start; + var control = attributes['points']['control'] || []; + var end; + var i, len; + + if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points + control = [control]; + } else { // break reference to attributes.points.control + var tmp = []; + for (i = 0, len = control.length; i< len; ++i) { + tmp[i] = control[i]; + } + control = tmp; + } + + if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative + Y.Dom.setStyle(el, 'position', 'relative'); + } + + if ( isset(attributes['points']['from']) ) { + Y.Dom.setXY(el, attributes['points']['from']); // set position to from point + } + else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position + + start = this.getAttribute('points'); // get actual top & left + + // TO beats BY, per SMIL 2.1 spec + if ( isset(attributes['points']['to']) ) { + end = translateValues.call(this, attributes['points']['to'], start); + + var pageXY = Y.Dom.getXY(this.getEl()); + for (i = 0, len = control.length; i < len; ++i) { + control[i] = translateValues.call(this, control[i], start); + } + + + } else if ( isset(attributes['points']['by']) ) { + end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ]; + + for (i = 0, len = control.length; i < len; ++i) { + control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ]; + } + } + + this.runtimeAttributes[attr] = [start]; + + if (control.length > 0) { + this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control); + } + + this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end; + } + else { + superclass.setRuntimeAttribute.call(this, attr); + } + }; + + var translateValues = function(val, start) { + var pageXY = Y.Dom.getXY(this.getEl()); + val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ]; + + return val; + }; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; +})(); +/** + * @class Anim subclass for scrolling elements to a position defined by the "scroll" member of "attributes". All "scroll" members are arrays with x, y scroll positions. + *

Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);

+ * @requires YAHOO.util.Anim + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Bezier + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String or HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ +(function() { + YAHOO.util.Scroll = function(el, attributes, duration, method) { + if (el) { // dont break existing subclasses not using YAHOO.extend + YAHOO.util.Scroll.superclass.constructor.call(this, el, attributes, duration, method); + } + }; + + YAHOO.extend(YAHOO.util.Scroll, YAHOO.util.ColorAnim); + + // shorthand + var Y = YAHOO.util; + var superclass = Y.Scroll.superclass; + var proto = Y.Scroll.prototype; + + /** + * toString method + * @return {String} string represenation of anim obj + */ + proto.toString = function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Scroll " + id); + }; + + /** + * Returns the value computed by the animation's "method". + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + proto.doMethod = function(attr, start, end) { + var val = null; + + if (attr == 'scroll') { + val = [ + this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames), + this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames) + ]; + + } else { + val = superclass.doMethod.call(this, attr, start, end); + } + return val; + }; + + /** + * Returns current value of the attribute. + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + proto.getAttribute = function(attr) { + var val = null; + var el = this.getEl(); + + if (attr == 'scroll') { + val = [ el.scrollLeft, el.scrollTop ]; + } else { + val = superclass.getAttribute.call(this, attr); + } + + return val; + }; + + /** + * Applies a value to an attribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + proto.setAttribute = function(attr, val, unit) { + var el = this.getEl(); + + if (attr == 'scroll') { + el.scrollLeft = val[0]; + el.scrollTop = val[1]; + } else { + superclass.setAttribute.call(this, attr, val, unit); + } + }; +})(); Index: openacs-4/packages/ajaxhelper/www/yui/autocomplete/README =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/autocomplete/Attic/README,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/autocomplete/README 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1,30 @@ +AutoComplete Release Notes + +*** version 0.11.0 *** + +* The method getListIds() has been deprecated for getListItems(), which returns +an array of DOM references. + +* All classnames have been prefixed with "yui-ac-". + +* Container elements should no longer have CSS property "display" set to "none". + +* The useIFrame property can now be set after instantiation. + +* On some browsers, the unmatchedItemSelectEvent may not be fired properly when +delimiter characters are defined. + +* On some browsers, defining delimiter characters while enabling forceSelection +may result in unexpected behavior. + + + +*** version 0.10.0 *** + +* Initial release + +* In order to enable the useIFrame property, it should be set in the +constructor. + +* On some browsers, defining delimiter characters while enabling forceSelection +may result in unexpected behavior. Index: openacs-4/packages/ajaxhelper/www/yui/autocomplete/autocomplete-debug.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/yui/autocomplete/Attic/autocomplete-debug.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ajaxhelper/www/yui/autocomplete/autocomplete-debug.js 20 Aug 2006 14:39:26 -0000 1.1 @@ -0,0 +1,5568 @@ +/* + +Copyright (c) 2006, Yahoo! Inc. All rights reserved. + +Code licensed under the BSD License: + +http://developer.yahoo.com/yui/license.txt + +version: 0.11.0 + +*/ + + + +/****************************************************************************/ + +/****************************************************************************/ + +/****************************************************************************/ + + + +/** + + * Class providing the customizable functionality of a plug-and-play DHTML + + * auto complete widget. Some key features: + + *
    + + *
  • Navigate with up/down arrow keys and/or mouse to pick a selection
  • + + *
  • The drop down container can "roll down" or "fly out" via configurable + + * animation
  • + + *
  • UI look-and-feel customizable through CSS, including container + + * attributes, borders, position, fonts, etc
  • + + *
+ + * + + * requires YAHOO.util.Dom Dom utility + + * requires YAHOO.util.Event Event utility + + * requires YAHOO.widget.DataSource Data source class + + * see YAHOO.util.Animation Animation utility + + * see JSON JSON library + + * + + * @constructor + + * @param {element | string} inputEl DOM element reference or string ID of the auto complete input field + + * @param {element | string} containerEl DOM element reference or string ID of the auto complete <div> + + * container + + * @param {object} oDataSource Instance of YAHOO.widget.DataSource for query/results + + * @param {object} oConfigs Optional object literal of config params + + */ + +YAHOO.widget.AutoComplete = function(inputEl,containerEl,oDataSource,oConfigs) { + + if(inputEl && containerEl && oDataSource) { + + // Validate data source + + if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) { + + this.dataSource = oDataSource; + + } + + else { + + YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString()); + + return; + + } + + + + // Validate input element + + if(YAHOO.util.Dom.inDocument(inputEl)) { + + if(typeof inputEl == "string") { + + this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + inputEl; + + this._oTextbox = document.getElementById(inputEl); + + } + + else { + + this._sName = (inputEl.id) ? + + "instance" + YAHOO.widget.AutoComplete._nIndex + " " + inputEl.id: + + "instance" + YAHOO.widget.AutoComplete._nIndex; + + this._oTextbox = inputEl; + + } + + } + + else { + + YAHOO.log("Could not instantiate AutoComplete due to an invalid input element", "error", this.toString()); + + return; + + } + + + + // Validate container element + + if(YAHOO.util.Dom.inDocument(containerEl)) { + + if(typeof containerEl == "string") { + + this._oContainer = document.getElementById(containerEl); + + } + + else { + + this._oContainer = containerEl; + + } + + if(this._oContainer.style.display == "none") { + + YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString()); + + } + + } + + else { + + YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString()); + + return; + + } + + + + // Set any config params passed in to override defaults + + if (typeof oConfigs == "object") { + + for(var sConfig in oConfigs) { + + if (sConfig) { + + this[sConfig] = oConfigs[sConfig]; + + } + + } + + } + + + + // Initialization sequence + + this._initContainer(); + + this._initProps(); + + this._initList(); + + this._initContainerHelpers(); + + + + // Set up events + + var oSelf = this; + + var oTextbox = this._oTextbox; + + // Events are actually for the content module within the container + + var oContent = this._oContainer._oContent; + + + + // Dom events + + YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf); + + YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf); + + YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf); + + YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf); + + YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf); + + YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf); + + YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf); + + YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf); + + YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf); + + if(oTextbox.form) { + + YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf); + + } + + + + // Custom events + + this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this); + + this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this); + + this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this); + + this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this); + + this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this); + + this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this); + + this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this); + + this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this); + + this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this); + + this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this); + + this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this); + + this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this); + + this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this); + + this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this); + + this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this); + + this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this); + + + + // Finish up + + oTextbox.setAttribute("autocomplete","off"); + + YAHOO.widget.AutoComplete._nIndex++; + + YAHOO.log("AutoComplete initialized","info",this.toString()); + + } + + // Required arguments were not found + + else { + + YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString()); + + } + +}; + + + +/*************************************************************************** + + * Public member variables + + ***************************************************************************/ + +/** + + * The data source object that encapsulates the data used for auto completion. + + * This object should be an inherited object from YAHOO.widget.DataSource. + + * + + * @type object + + */ + +YAHOO.widget.AutoComplete.prototype.dataSource = null; + + + +/** + + * Number of characters that must be entered before querying for results. + + * Default: 1. + + * + + * @type number + + */ + +YAHOO.widget.AutoComplete.prototype.minQueryLength = 1; + + + +/** + + * Maximum number of results to display in auto complete container. Default: 10. + + * + + * @type number + + */ + +YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10; + + + +/** + + * Number of seconds to delay before submitting a query request. If a query + + * request is received before a previous one has completed its delay, the + + * previous request is cancelled and the new request is set to the delay. + + * Default: 0.5. + + * + + * @type number + + */ + +YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5; + + + +/** + + * Class name of a highlighted item within the auto complete container. + + * Default: "yui-ac-highlight". + + * + + * @type string + + */ + +YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight"; + + + +/** + + * Class name of a pre-highlighted item within the auto complete container. + + * Default: null. + + * + + * @type string + + */ + +YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null; + + + +/** + + * Query delimiter. A single character separator for multiple delimited + + * selections. Multiple delimiter characteres may be defined as an array of + + * strings. A null value or empty string indicates that query results cannot + + * be delimited. This feature is not recommended if you need forceSelection to + + * be true. Default: null. + + * + + * @type string or array + + */ + +YAHOO.widget.AutoComplete.prototype.delimChar = null; + + + +/** + + * Whether or not the first item in the auto complete container should be + + * automatically highlighted on expand. Default: true. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.autoHighlight = true; + + + +/** + + * Whether or not the auto complete input field should be automatically updated + + * with the first query result as the user types, auto-selecting the substring + + * that the user has not typed. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.typeAhead = false; + + + +/** + + * Whether or not to animate the expansion/collapse of the auto complete + + * container in the horizontal direction. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.animHoriz = false; + + + +/** + + * Whether or not to animate the expansion/collapse of the auto complete + + * container in the vertical direction. Default: true. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.animVert = true; + + + +/** + + * Speed of container expand/collapse animation, in seconds. Default: 0.3. + + * + + * @type number + + */ + +YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3; + + + +/** + + * Whether or not to force the user's selection to match one of the query + + * results. Enabling this feature essentially transforms the auto complete form + + * input field into a <select> field. This feature is not recommended + + * with delimiter character(s) defined. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.forceSelection = false; + + + +/** + + * Whether or not to allow browsers to cache user-typed input in the input + + * field. Disabling this feature will prevent the widget from setting the + + * autocomplete="off" on the auto complete input field. When autocomplete="off" + + * and users click the back button after form submission, user-typed input can + + * be prefilled by the browser from its cache. This caching of user input may + + * not be desired for sensitive data, such as credit card numbers, in which + + * case, implementers should consider setting allowBrowserAutocomplete to false. + + * Default: true. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true; + + + +/** + + * Whether or not the auto complete container should always be displayed. + + * Enabling this feature prevents the toggling of the container to a collapsed + + * state. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false; + + + +/** + + * Whether or not to use an iFrame to layer over Windows form elements in + + * IE. Set to true only when the auto complete container will be on top of a + + * <select> field in IE and thus exposed to the IE z-index bug (i.e., + + * 5.5 < IE < 7). Default:false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.useIFrame = false; + + + +/** + + * Configurable iFrame src used when useIFrame = true. Implementations over SSL + + * should set this parameter to an appropriate https location in order to avoid + + * security-related browser errors. Default:"about:blank". + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.iFrameSrc = "about:blank"; + + + +/** + + * Whether or not the auto complete container should have a shadow. Default:false. + + * + + * @type boolean + + */ + +YAHOO.widget.AutoComplete.prototype.useShadow = false; + + + +/*************************************************************************** + + * Public methods + + ***************************************************************************/ + + /** + + * Public accessor to the unique name of the auto complete instance. + + * + + * @return {string} Unique name of the auto complete instance + + */ + +YAHOO.widget.AutoComplete.prototype.getName = function() { + + return this._sName; + +}; + + + + /** + + * Public accessor to the unique name of the auto complete instance. + + * + + * @return {string} Unique name of the auto complete instance + + */ + +YAHOO.widget.AutoComplete.prototype.toString = function() { + + return "AutoComplete " + this._sName; + +}; + + + +/** + + * Public accessor to the internal array of DOM <li> elements that + + * display query results within the auto complete container. + + * + + * @return {array} Array of <li> elements within the auto complete + + * container + + */ + +YAHOO.widget.AutoComplete.prototype.getListItems = function() { + + return this._aListItems; + +}; + + + +/** + + * Public accessor to the data held in an <li> element of the + + * auto complete container. + + * + + * @return {object or array} Object or array of result data or null + + */ + +YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) { + + if(oListItem._oResultData) { + + return oListItem._oResultData; + + } + + else { + + return false; + + } + +}; + + + +/** + + * Sets HTML markup for the auto complete container header. This markup will be + + * inserted within a <div> tag with a class of "ac_hd". + + * + + * @param {string} sHeader HTML markup for container header + + */ + +YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) { + + if(sHeader) { + + if(this._oContainer._oContent._oHeader) { + + this._oContainer._oContent._oHeader.innerHTML = sHeader; + + this._oContainer._oContent._oHeader.style.display = "block"; + + } + + } + + else { + + this._oContainer._oContent._oHeader.innerHTML = ""; + + this._oContainer._oContent._oHeader.style.display = "none"; + + } + +}; + + + +/** + + * Sets HTML markup for the auto complete container footer. This markup will be + + * inserted within a <div> tag with a class of "ac_ft". + + * + + * @param {string} sFooter HTML markup for container footer + + */ + +YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) { + + if(sFooter) { + + if(this._oContainer._oContent._oFooter) { + + this._oContainer._oContent._oFooter.innerHTML = sFooter; + + this._oContainer._oContent._oFooter.style.display = "block"; + + } + + } + + else { + + this._oContainer._oContent._oFooter.innerHTML = ""; + + this._oContainer._oContent._oFooter.style.display = "none"; + + } + +}; + + + +/** + + * Sets HTML markup for the auto complete container body. This markup will be + + * inserted within a <div> tag with a class of "ac_bd". + + * + + * @param {string} sHeader HTML markup for container body + + */ + +YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) { + + if(sBody) { + + if(this._oContainer._oContent._oBody) { + + this._oContainer._oContent._oBody.innerHTML = sBody; + + this._oContainer._oContent._oBody.style.display = "block"; + + this._oContainer._oContent.style.display = "block"; + + } + + } + + else { + + this._oContainer._oContent._oBody.innerHTML = ""; + + this._oContainer._oContent.style.display = "none"; + + } + + this._maxResultsDisplayed = 0; + +}; + + + +/** + + * Overridable method that converts a result item object into HTML markup + + * for display. Return data values are accessible via the oResultItem object, + + * and the key return value will always be oResultItem[0]. Markup will be + + * displayed within <li> element tags in the container. + + * + + * @param {object} oResultItem Result item object representing one query result + + * @param {string} sQuery The current query string + + * @return {string} HTML markup of formatted result data + + */ + +YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) { + + var sResult = oResultItem[0]; + + if(sResult) { + + return sResult; + + } + + else { + + return ""; + + } + +}; + + + +/** + + * Makes query request to the data source. + + * + + * @param {string} sQuery Query string. + + */ + +YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) { + + if(sQuery) { + + this._sendQuery(sQuery); + + } + + else { + + YAHOO.log("Query could not be sent because the string value was empty or null.","warn",this.toString()); + + return; + + } + +}; + + + +/*************************************************************************** + + * Events + + ***************************************************************************/ + +/** + + * Fired when the auto complete text input box receives focus. Subscribers + + * receive the following array:
+ + * - args[0] The auto complete object instance + + */ + +YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null; + + + +/** + + * Fired when the auto complete text input box receives key input. Subscribers + + * receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The keycode number + + */ + +YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null; + + + +/** + + * Fired when the auto complete instance makes a query to the data source. + + * Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The query string + + */ + +YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null; + + + +/** + + * Fired when the auto complete instance receives query results from the data + + * source. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The query string + + * - args[2] Results array + + */ + +YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null; + + + +/** + + * Fired when the auto complete instance does not receive query results from the + + * data source due to an error. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The query string + + */ + +YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null; + + + +/** + + * Fired when the auto complete container is expanded. If alwaysShowContainer is + + * enabled, then containerExpandEvent will be fired when the container is + + * populated with results. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + */ + +YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null; + + + +/** + + * Fired when the auto complete textbox has been prefilled by the type-ahead + + * feature. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The query string + + * - args[2] The prefill string + + */ + +YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null; + + + +/** + + * Fired when result item has been moused over. Subscribers receive the following + + * array:
+ + * - args[0] The auto complete object instance + + * - args[1] The <li> element item moused to + + */ + +YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null; + + + +/** + + * Fired when result item has been moused out. Subscribers receive the + + * following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The <li> element item moused from + + */ + +YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null; + + + +/** + + * Fired when result item has been arrowed to. Subscribers receive the following + + * array:
+ + * - args[0] The auto complete object instance + + * - args[1] The <li> element item arrowed to + + */ + +YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null; + + + +/** + + * Fired when result item has been arrowed away from. Subscribers receive the + + * following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The <li> element item arrowed from + + */ + +YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null; + + + +/** + + * Fired when an item is selected via mouse click, ENTER key, or TAB key. + + * Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The selected <li> element item + + * - args[2] The data returned for the item, either as an object, or mapped from the schema into an array + + */ + +YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null; + + + +/** + + * Fired when an user selection does not match any of the displayed result items. + + * Note that this event may not behave as expected when delimiter characters + + * have been defined. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + * - args[1] The user selection + + */ + +YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null; + + + +/** + + * Fired if forceSelection is enabled and the user's input has been cleared + + * because it did not match one of the returned query results. Subscribers + + * receive the following array:
+ + * - args[0] The auto complete object instance + + */ + +YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null; + + + +/** + + * Fired when the auto complete container is collapsed. If alwaysShowContainer is + + * enabled, then containerCollapseEvent will be fired when the container is + + * cleared of results. Subscribers receive the following array:
+ + * - args[0] The auto complete object instance + + */ + +YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null; + + + +/** + + * Fired when the auto complete text input box loses focus. Subscribers receive + + * an array of the following array:
+ + * - args[0] The auto complete object instance + + */ + +YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null; + + + +/*************************************************************************** + + * Private member variables + + ***************************************************************************/ + +/** + + * Internal class variable to index multiple auto complete instances. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.AutoComplete._nIndex = 0; + + + +/** + + * Name of auto complete instance. + + * + + * @type string + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._sName = null; + + + +/** + + * Text input box DOM element. + + * + + * @type object + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._oTextbox = null; + + + +/** + + * Whether or not the textbox is currently in focus. If query results come back + + * but the user has already moved on, do not proceed with auto complete behavior. + + * + + * @type boolean + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._bFocused = true; + + + +/** + + * Animation instance for container expand/collapse. + + * + + * @type boolean + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._oAnim = null; + + + +/** + + * Container DOM element. + + * + + * @type object + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._oContainer = null; + + + +/** + + * Whether or not the auto complete container is currently open. + + * + + * @type boolean + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._bContainerOpen = false; + + + +/** + + * Whether or not the mouse is currently over the auto complete + + * container. This is necessary in order to prevent clicks on container items + + * from being text input box blur events. + + * + + * @type boolean + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._bOverContainer = false; + + + +/** + + * Array of <li> elements references that contain query results within the + + * auto complete container. + + * + + * @type array + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._aListItems = null; + + + +/** + + * Number of <li> elements currently displayed in auto complete container. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0; + + + +/** + + * Internal count of <li> elements displayed and hidden in auto complete container. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0; + + + +/** + + * Current query string + + * + + * @type string + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._sCurQuery = null; + + + +/** + + * Past queries this session (for saving delimited queries). + + * + + * @type string + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._sSavedQuery = null; + + + +/** + + * Pointer to the currently highlighted <li> element in the container. + + * + + * @type object + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._oCurItem = null; + + + +/** + + * Whether or not an item has been selected since the container was populated + + * with results. Reset to false by _populateList, and set to true when item is + + * selected. + + * + + * @type boolean + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._bItemSelected = false; + + + +/** + + * Key code of the last key pressed in textbox. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._nKeyCode = null; + + + +/** + + * Delay timeout ID. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._nDelayID = -1; + + + +/*************************************************************************** + + * Private methods + + ***************************************************************************/ + +/** + + * Updates and validates latest public config properties. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._initProps = function() { + + // Correct any invalid values + + var minQueryLength = this.minQueryLength; + + if(isNaN(minQueryLength) || (minQueryLength < 1)) { + + minQueryLength = 1; + + } + + var maxResultsDisplayed = this.maxResultsDisplayed; + + if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) { + + this.maxResultsDisplayed = 10; + + } + + var queryDelay = this.queryDelay; + + if(isNaN(this.queryDelay) || (this.queryDelay < 0)) { + + this.queryDelay = 0.5; + + } + + var aDelimChar = (this.delimChar) ? this.delimChar : null; + + if(aDelimChar) { + + if(typeof aDelimChar == "string") { + + this.delimChar = [aDelimChar]; + + } + + else if(aDelimChar.constructor != Array) { + + this.delimChar = null; + + } + + } + + var animSpeed = this.animSpeed; + + if((this.animHoriz || this.animVert) && YAHOO.util.Anim) { + + if(isNaN(animSpeed) || (animSpeed < 0)) { + + animSpeed = 0.3; + + } + + if(!this._oAnim ) { + + oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed); + + this._oAnim = oAnim; + + } + + else { + + this._oAnim.duration = animSpeed; + + } + + } + + if(this.forceSelection && this.delimChar) { + + YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString()); + + } + + if(this.alwaysShowContainer && (this.useShadow || this.useIFrame)) { + + YAHOO.log("The features useShadow and useIFrame are not compatible with the alwaysShowContainer feature.","warn", this.toString()); + + } + + + + if(this.alwaysShowContainer) { + + this._bContainerOpen = true; + + } + +}; + + + +/** + + * Initializes the auto complete container helpers if they are enabled and do + + * not exist + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() { + + if(this.useShadow && !this._oContainer._oShadow) { + + var oShadow = document.createElement("div"); + + oShadow.className = "yui-ac-shadow"; + + this._oContainer._oShadow = this._oContainer.appendChild(oShadow); + + } + + if(this.useIFrame && !this._oContainer._oIFrame) { + + var oIFrame = document.createElement("iframe"); + + oIFrame.src = this.iFrameSrc; + + oIFrame.frameBorder = 0; + + oIFrame.scrolling = "no"; + + oIFrame.style.position = "absolute"; + + oIFrame.style.width = "100%"; + + oIFrame.style.height = "100%"; + + this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame); + + } + +}; + + + +/** + + * Initializes the auto complete container once at object creation + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._initContainer = function() { + + if(!this._oContainer._oContent) { + + // The oContent div helps size the iframe and shadow properly + + var oContent = document.createElement("div"); + + oContent.className = "yui-ac-content"; + + oContent.style.display = "none"; + + this._oContainer._oContent = this._oContainer.appendChild(oContent); + + + + var oHeader = document.createElement("div"); + + oHeader.className = "yui-ac-hd"; + + oHeader.style.display = "none"; + + this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader); + + + + var oBody = document.createElement("div"); + + oBody.className = "yui-ac-bd"; + + this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody); + + + + var oFooter = document.createElement("div"); + + oFooter.className = "yui-ac-ft"; + + oFooter.style.display = "none"; + + this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter); + + } + + else { + + YAHOO.log("Could not initialize the container","warn",this.toString()); + + } + +}; + + + +/** + + * Clears out contents of container body and creates up to + + * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an + + * <ul> element. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._initList = function() { + + this._aListItems = []; + + while(this._oContainer._oContent._oBody.hasChildNodes()) { + + var oldListItems = this.getListItems(); + + if(oldListItems) { + + for(var oldi = oldListItems.length-1; oldi >= 0; i--) { + + oldListItems[oldi] = null; + + } + + } + + this._oContainer._oContent._oBody.innerHTML = ""; + + } + + + + var oList = document.createElement("ul"); + + oList = this._oContainer._oContent._oBody.appendChild(oList); + + for(var i=0; i 0) { + + var nDelayID = + + setTimeout(function(){oSelf._sendQuery(sText);},(oSelf.queryDelay * 1000)); + + + + if (oSelf._nDelayID != -1) { + + clearTimeout(oSelf._nDelayID); + + } + + + + oSelf._nDelayID = nDelayID; + + } + + else { + + // No delay so send request immediately + + oSelf._sendQuery(sText); + + } + +}; + + + +/** + + * Whether or not key is functional or should be ignored. Note that the right + + * arrow key is NOT an ignored key since it triggers queries for certain intl + + * charsets. + + * + + * @param {number} nKeycode Code of key pressed + + * @return {boolean} Whether or not to be ignore key + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) { + + if ((nKeyCode == 9) || (nKeyCode == 13) || // tab, enter + + (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl + + (nKeyCode >= 18 && nKeyCode <= 20) || // alt,pause/break,caps lock + + (nKeyCode == 27) || // esc + + (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end + + (nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up + + (nKeyCode == 40) || // down + + (nKeyCode >= 44 && nKeyCode <= 45)) { // print screen,insert + + return true; + + } + + return false; + +}; + + + +/** + + * Handles text input box receiving focus. + + * + + * @param {event} v The focus event + + * @param {object} oSelf The auto complete instance + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) { + + oSelf._oTextbox.setAttribute("autocomplete","off"); + + oSelf._bFocused = true; + + oSelf.textboxFocusEvent.fire(oSelf); + +}; + + + +/** + + * Handles text input box losing focus. + + * + + * @param {event} v The focus event + + * @param {object} oSelf The auto complete instance + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) { + + // Don't treat as a blur if it was a selection via mouse click + + if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) { + + // Current query needs to be validated + + if(!oSelf._bItemSelected) { + + if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && !oSelf._textMatchesOption())) { + + if(oSelf.forceSelection) { + + oSelf._clearSelection(); + + } + + else { + + oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery); + + } + + } + + } + + + + if(oSelf._bContainerOpen) { + + oSelf._clearList(); + + } + + oSelf._bFocused = false; + + oSelf.textboxBlurEvent.fire(oSelf); + + } + +}; + + + +/** + + * Handles form submission event. + + * + + * @param {event} v The submit event + + * @param {object} oSelf The auto complete instance + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._onFormSubmit = function(v,oSelf) { + + if(oSelf.allowBrowserAutocomplete) { + + oSelf._oTextbox.setAttribute("autocomplete","on"); + + } + + else { + + oSelf._oTextbox.setAttribute("autocomplete","off"); + + } + +}; + + + +/** + + * Makes query request to the data source. + + * + + * @param {string} sQuery Query string. + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) { + + // Delimiter has been enabled + + var aDelimChar = (this.delimChar) ? this.delimChar : null; + + if(aDelimChar) { + + // Loop through all possible delimiters and find the latest one + + // A " " may be a false positive if they are defined as delimiters AND + + // are used to separate delimited queries + + var nDelimIndex = -1; + + for(var i = aDelimChar.length-1; i >= 0; i--) { + + var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]); + + if(nNewIndex > nDelimIndex) { + + nDelimIndex = nNewIndex; + + } + + } + + // If we think the last delimiter is a space (" "), make sure it is NOT + + // a false positive by also checking the char directly before it + + if(aDelimChar[i] == " ") { + + for (var j = aDelimChar.length-1; j >= 0; j--) { + + if(sQuery[nDelimIndex - 1] == aDelimChar[j]) { + + nDelimIndex--; + + break; + + } + + } + + } + + // A delimiter has been found so extract the latest query + + if (nDelimIndex > -1) { + + var nQueryStart = nDelimIndex + 1; + + // Trim any white space from the beginning... + + while(sQuery.charAt(nQueryStart) == " ") { + + nQueryStart += 1; + + } + + // ...and save the rest of the string for later + + this._sSavedQuery = sQuery.substring(0,nQueryStart); + + // Here is the query itself + + sQuery = sQuery.substr(nQueryStart); + + } + + else if(sQuery.indexOf(this._sSavedQuery) < 0){ + + this._sSavedQuery = null; + + } + + } + + + + // Don't search queries that are too short + + if (sQuery.length < this.minQueryLength) { + + if (this._nDelayID != -1) { + + clearTimeout(this._nDelayID); + + } + + this._clearList(); + + return; + + } + + + + sQuery = encodeURIComponent(sQuery); + + this._nDelayID = -1; // Reset timeout ID because request has been made + + this.dataRequestEvent.fire(this, sQuery); + + this.dataSource.getResults(this._populateList, sQuery, this); + +}; + + + +/** + + * Hides all visuals related to the array of <li> elements in the container. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._clearList = function() { + + this._oContainer._oContent.scrollTop = 0; + + var aItems = this._aListItems; + + + + if(aItems && (aItems.length > 0)) { + + for(var i = aItems.length-1; i >= 0 ; i--) { + + aItems[i].style.display = "none"; + + } + + } + + + + if (this._oCurItem) { + + this._toggleHighlight(this._oCurItem,"from"); + + } + + + + this._oCurItem = null; + + this._nDisplayedItems = 0; + + this._sCurQuery = null; + + this._toggleContainer(false); + +}; + + + +/** + + * Populates the array of <li> elements in the container with query + + * results. This method is passed to YAHOO.widget.DataSource#getResults as a + + * callback function so results from the datasource are returned to the + + * auto complete instance. + + * + + * @param {string} sQuery The query string + + * @param {object} aResults An array of query result objects from the data source + + * @param {string} oSelf The auto complete instance + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, aResults, oSelf) { + + if(aResults === null) { + + oSelf.dataErrorEvent.fire(oSelf, sQuery); + + } + + if (!oSelf._bFocused || !aResults) { + + return; + + } + + + + var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1); + + var contentStyle = oSelf._oContainer._oContent.style; + + contentStyle.width = (!isOpera) ? null : ""; + + contentStyle.height = (!isOpera) ? null : ""; + + + + var sCurQuery = decodeURIComponent(sQuery); + + oSelf._sCurQuery = sCurQuery; + + oSelf._bItemSelected = false; + + + + if(oSelf._maxResultsDisplayed != oSelf.maxResultsDisplayed) { + + oSelf._initList(); + + } + + + + var nItems = Math.min(aResults.length,oSelf.maxResultsDisplayed); + + oSelf._nDisplayedItems = nItems; + + if (nItems > 0) { + + oSelf._initContainerHelpers(); + + var aItems = oSelf._aListItems; + + + + // Fill items with data + + for(var i = nItems-1; i >= 0; i--) { + + var oItemi = aItems[i]; + + var oResultItemi = aResults[i]; + + oItemi.innerHTML = oSelf.formatResult(oResultItemi, sCurQuery); + + oItemi.style.display = "list-item"; + + oItemi._sResultKey = oResultItemi[0]; + + oItemi._oResultData = oResultItemi; + + + + } + + + + // Empty out remaining items if any + + for(var j = aItems.length-1; j >= nItems ; j--) { + + var oItemj = aItems[j]; + + oItemj.innerHTML = null; + + oItemj.style.display = "none"; + + oItemj._sResultKey = null; + + oItemj._oResultData = null; + + } + + + + if(oSelf.autoHighlight) { + + // Go to the first item + + var oFirstItem = aItems[0]; + + oSelf._toggleHighlight(oFirstItem,"to"); + + oSelf.itemArrowToEvent.fire(oSelf, oFirstItem); + + oSelf._typeAhead(oFirstItem,sQuery); + + } + + else { + + oSelf._oCurItem = null; + + } + + + + // Expand the container + + oSelf._toggleContainer(true); + + } + + else { + + oSelf._clearList(); + + } + + oSelf.dataReturnEvent.fire(oSelf, sQuery, aResults); + +}; + + + +/** + + * When YAHOO.widget.AutoComplete#bForceSelection is true and the user attempts + + * leave the text input box without selecting an item from the query results, + + * the user selection is cleared. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._clearSelection = function() { + + var sValue = this._oTextbox.value; + + var sChar = (this.delimChar) ? this.delimChar[0] : null; + + var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1; + + if(nIndex > -1) { + + this._oTextbox.value = sValue.substring(0,nIndex); + + } + + else { + + this._oTextbox.value = ""; + + } + + this._sSavedQuery = this._oTextbox.value; + + + + // Fire custom event + + this.selectionEnforceEvent.fire(this); + +}; + + + +/** + + * Whether or not user-typed value in the text input box matches any of the + + * query results. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() { + + var foundMatch = false; + + + + for(var i = this._nDisplayedItems-1; i >= 0 ; i--) { + + var oItem = this._aListItems[i]; + + var sMatch = oItem._sResultKey.toLowerCase(); + + if (sMatch == this._sCurQuery.toLowerCase()) { + + foundMatch = true; + + break; + + } + + } + + return(foundMatch); + +}; + + + +/** + + * Updates in the text input box with the first query result as the user types, + + * selecting the substring that the user has not typed. + + * + + * @param {object} oItem The <li> element item whose data populates the input field + + * @param {string} sQuery Query string + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._typeAhead = function(oItem, sQuery) { + + // Don't update if turned off + + if (!this.typeAhead) { + + return; + + } + + + + var oTextbox = this._oTextbox; + + var sValue = this._oTextbox.value; // any saved queries plus what user has typed + + + + // Don't update with type-ahead if text selection is not supported + + if(!oTextbox.setSelectionRange && !oTextbox.createTextRange) { + + return; + + } + + + + // Select the portion of text that the user has not typed + + var nStart = sValue.length; + + this._updateValue(oItem); + + var nEnd = oTextbox.value.length; + + this._selectText(oTextbox,nStart,nEnd); + + var sPrefill = oTextbox.value.substr(nStart,nEnd); + + this.typeAheadEvent.fire(this,sQuery,sPrefill); + +}; + + + +/** + + * Selects text in a text input box. + + * + + * @param {object} oTextbox Text input box element in which to select text + + * @param {number} nStart Starting index of text string to select + + * @param {number} nEnd Ending index of text selection + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._selectText = function(oTextbox, nStart, nEnd) { + + if (oTextbox.setSelectionRange) { // For Mozilla + + oTextbox.setSelectionRange(nStart,nEnd); + + } + + else if (oTextbox.createTextRange) { // For IE + + var oTextRange = oTextbox.createTextRange(); + + oTextRange.moveStart("character", nStart); + + oTextRange.moveEnd("character", nEnd-oTextbox.value.length); + + oTextRange.select(); + + } + + else { + + oTextbox.select(); + + } + +}; + + + +/** + + * Syncs auto complete container with its helpers. + + * + + * @param {boolean} bShow True if container is expanded, false if collapsed + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) { + + var bFireEvent = false; + + var width = this._oContainer._oContent.offsetWidth + "px"; + + var height = this._oContainer._oContent.offsetHeight + "px"; + + + + if(this.useIFrame && this._oContainer._oIFrame) { + + bFireEvent = true; + + if(this.alwaysShowContainer || bShow) { + + this._oContainer._oIFrame.style.width = width; + + this._oContainer._oIFrame.style.height = height; + + } + + else { + + this._oContainer._oIFrame.style.width = 0; + + this._oContainer._oIFrame.style.height = 0; + + } + + } + + if(this.useShadow && this._oContainer._oShadow) { + + bFireEvent = true; + + if(this.alwaysShowContainer || bShow) { + + this._oContainer._oShadow.style.width = width; + + this._oContainer._oShadow.style.height = height; + + } + + else { + + this._oContainer._oShadow.style.width = 0; + + this._oContainer._oShadow.style.height = 0; + + } + + } + +}; + + + +/** + + * Animates expansion or collapse of the container. + + * + + * @param {boolean} bShow True if container should be expanded, false if + + * container should be collapsed + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) { + + // Implementer has container always open so don't mess with it + + if(this.alwaysShowContainer) { + + // Fire these events to give implementers a hook into the container + + // being populated and being emptied + + if(bShow) { + + this.containerExpandEvent.fire(this); + + } + + else { + + this.containerCollapseEvent.fire(this); + + } + + this._bContainerOpen = bShow; + + return; + + } + + + + var oContainer = this._oContainer; + + // Container is already closed + + if (!bShow && !this._bContainerOpen) { + + oContainer._oContent.style.display = "none"; + + return; + + } + + + + // If animation is enabled... + + var oAnim = this._oAnim; + + if (oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) { + + // If helpers need to be collapsed, do it right away... + + // but if helpers need to be expanded, wait until after the container expands + + if(!bShow) { + + this._toggleContainerHelpers(bShow); + + } + + + + if(oAnim.isAnimated()) { + + oAnim.stop(); + + } + + + + // Clone container to grab current size offscreen + + var oClone = oContainer._oContent.cloneNode(true); + + oContainer.appendChild(oClone); + + oClone.style.top = "-9000px"; + + oClone.style.display = "block"; + + + + // Current size of the container is the EXPANDED size + + var wExp = oClone.offsetWidth; + + var hExp = oClone.offsetHeight; + + + + // Calculate COLLAPSED sizes based on horiz and vert anim + + var wColl = (this.animHoriz) ? 0 : wExp; + + var hColl = (this.animVert) ? 0 : hExp; + + + + // Set animation sizes + + oAnim.attributes = (bShow) ? + + {width: { to: wExp }, height: { to: hExp }} : + + {width: { to: wColl}, height: { to: hColl }}; + + + + // If opening anew, set to a collapsed size... + + if(bShow && !this._bContainerOpen) { + + oContainer._oContent.style.width = wColl+"px"; + + oContainer._oContent.style.height = hColl+"px"; + + } + + // Else, set it to its last known size. + + else { + + oContainer._oContent.style.width = wExp+"px"; + + oContainer._oContent.style.height = hExp+"px"; + + } + + + + oContainer.removeChild(oClone); + + oClone = null; + + + + var oSelf = this; + + var onAnimComplete = function() { + + // Finish the collapse + + oAnim.onComplete.unsubscribeAll(); + + + + if(bShow) { + + oSelf.containerExpandEvent.fire(oSelf); + + } + + else { + + oContainer._oContent.style.display = "none"; + + oSelf.containerCollapseEvent.fire(oSelf); + + } + + oSelf._toggleContainerHelpers(bShow); + + }; + + + + // Display container and animate it + + oContainer._oContent.style.display = "block"; + + oAnim.onComplete.subscribe(onAnimComplete); + + oAnim.animate(); + + this._bContainerOpen = bShow; + + } + + // Else don't animate, just show or hide + + else { + + if(bShow) { + + oContainer._oContent.style.display = "block"; + + this.containerExpandEvent.fire(this); + + } + + else { + + oContainer._oContent.style.display = "none"; + + this.containerCollapseEvent.fire(this); + + } + + this._toggleContainerHelpers(bShow); + + this._bContainerOpen = bShow; + + } + + + +}; + + + +/** + + * Toggles the highlight on or off for an item in the container, and also cleans + + * up highlighting of any previous item. + + * + + * @param {object} oNewItem New The <li> element item to receive highlight + + * behavior + + * @param {string} sType "mouseover" will toggle highlight on, and "mouseout" + + * will toggle highlight off. + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(oNewItem, sType) { + + var sHighlight = this.highlightClassName; + + if(this._oCurItem) { + + // Remove highlight from old item + + YAHOO.util.Dom.removeClass(this._oCurItem, sHighlight); + + } + + + + if((sType == "to") && sHighlight) { + + // Apply highlight to new item + + YAHOO.util.Dom.addClass(oNewItem, sHighlight); + + this._oCurItem = oNewItem; + + } + +}; + + + +/** + + * Toggles the pre-highlight on or off for an item in the container. + + * + + * @param {object} oNewItem New The <li> element item to receive highlight + + * behavior + + * @param {string} sType "mouseover" will toggle highlight on, and "mouseout" + + * will toggle highlight off. + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(oNewItem, sType) { + + if(oNewItem == this._oCurItem) { + + return; + + } + + + + var sPrehighlight = this.prehighlightClassName; + + if((sType == "mouseover") && sPrehighlight) { + + // Apply prehighlight to new item + + YAHOO.util.Dom.addClass(oNewItem, sPrehighlight); + + } + + else { + + // Remove prehighlight from old item + + YAHOO.util.Dom.removeClass(oNewItem, sPrehighlight); + + } + +}; + + + +/** + + * Updates the text input box value with selected query result. If a delimiter + + * has been defined, then the value gets appended with the delimiter. + + * + + * @param {object} oItem The <li> element item with which to update the value + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._updateValue = function(oItem) { + + var oTextbox = this._oTextbox; + + var sDelimChar = (this.delimChar) ? this.delimChar[0] : null; + + var sSavedQuery = this._sSavedQuery; + + var sResultKey = oItem._sResultKey; + + oTextbox.focus(); + + + + // First clear text field + + oTextbox.value = ""; + + // Grab data to put into text field + + if(sDelimChar) { + + if(sSavedQuery) { + + oTextbox.value = sSavedQuery; + + } + + oTextbox.value += sResultKey + sDelimChar; + + if(sDelimChar != " ") { + + oTextbox.value += " "; + + } + + } + + else { oTextbox.value = sResultKey; } + + + + // scroll to bottom of textarea if necessary + + if(oTextbox.type == "textarea") { + + oTextbox.scrollTop = oTextbox.scrollHeight; + + } + + + + // move cursor to end + + var end = oTextbox.value.length; + + this._selectText(oTextbox,end,end); + + + + this._oCurItem = oItem; + +}; + + + +/** + + * Selects a result item from the container + + * + + * @param {object} oItem The selected <li> element item + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._selectItem = function(oItem) { + + this._bItemSelected = true; + + this._updateValue(oItem); + + this.itemSelectEvent.fire(this, oItem, oItem._oResultData); + + this._clearList(); + +}; + + + +/** + + * For values updated by type-ahead, the right arrow key jumps to the end + + * of the textbox, otherwise the container is closed. + + * + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._jumpSelection = function() { + + if(!this.typeAhead) { + + return; + + } + + else { + + this._clearList(); + + } + +}; + + + +/** + + * Triggered by up and down arrow keys, changes the current highlighted + + * <li> element item. Scrolls container if necessary. + + * + + * @param {number} nKeyCode Code of key pressed + + * @private + + */ + +YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) { + + if(this._bContainerOpen) { + + // Determine current item's id number + + var oCurItem = this._oCurItem; + + var nCurItemIndex = -1; + + + + if (oCurItem) { + + nCurItemIndex = oCurItem._nItemIndex; + + } + + + + var nNewItemIndex = (nKeyCode == 40) ? + + (nCurItemIndex + 1) : (nCurItemIndex - 1); + + + + // Out of bounds + + if (nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) { + + return; + + } + + + + if (oCurItem) { + + // Unhighlight current item + + this._toggleHighlight(oCurItem, "from"); + + this.itemArrowFromEvent.fire(this, oCurItem); + + } + + if (nNewItemIndex == -1) { + + // Go back to query (remove type-ahead string) + + if(this.delimChar && this._sSavedQuery) { + + if (!this._textMatchesOption()) { + + this._oTextbox.value = this._sSavedQuery; + + } + + else { + + this._oTextbox.value = this._sSavedQuery + this._sCurQuery; + + } + + } + + else { + + this._oTextbox.value = this._sCurQuery; + + } + + this._oCurItem = null; + + return; + + } + + if (nNewItemIndex == -2) { + + // Close container + + this._clearList(); + + return; + + } + + + + var oNewItem = this._aListItems[nNewItemIndex]; + + + + // Scroll the container if necessary + + if((YAHOO.util.Dom.getStyle(this._oContainer._oContent,"overflow") == "auto") && + + (nNewItemIndex > -1) && (nNewItemIndex < this._nDisplayedItems)) { + + // User is keying down + + if(nKeyCode == 40) { + + // Bottom of selected item is below scroll area... + + if((oNewItem.offsetTop+oNewItem.offsetHeight) > (this._oContainer._oContent.scrollTop + this._oContainer._oContent.offsetHeight)) { + + // Set bottom of scroll area to bottom of selected item + + this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - this._oContainer._oContent.offsetHeight; + + } + + // Bottom of selected item is above scroll area... + + else if((oNewItem.offsetTop+oNewItem.offsetHeight) < this._oContainer._oContent.scrollTop) { + + // Set top of selected item to top of scroll area + + this._oContainer._oContent.scrollTop = oNewItem.offsetTop; + + + + } + + } + + // User is keying up + + else { + + // Top of selected item is above scroll area + + if(oNewItem.offsetTop < this._oContainer._oContent.scrollTop) { + + // Set top of scroll area to top of selected item + + this._oContainer._oContent.scrollTop = oNewItem.offsetTop; + + } + + // Top of selected item is below scroll area + + else if(oNewItem.offsetTop > (this._oContainer._oContent.scrollTop + this._oContainer._oContent.offsetHeight)) { + + // Set bottom of selected item to bottom of scroll area + + this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - this._oContainer._oContent.offsetHeight; + + } + + } + + } + + + + this._toggleHighlight(oNewItem, "to"); + + this.itemArrowToEvent.fire(this, oNewItem); + + if(this.typeAhead) { + + this._updateValue(oNewItem); + + } + + } + +}; + + + +/****************************************************************************/ + +/****************************************************************************/ + +/****************************************************************************/ + + + +/** + + * Class providing encapsulation of a data source. + + * + + * @constructor + + * + + */ + +YAHOO.widget.DataSource = function() { + + /* abstract class */ + +}; + + + + + +/*************************************************************************** + + * Public constants + + ***************************************************************************/ + +/** + + * Error message for null data responses. + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DataSource.prototype.ERROR_DATANULL = "Response data was null"; + + + +/** + + * Error message for data responses with parsing errors. + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DataSource.prototype.ERROR_DATAPARSE = "Response data could not be parsed"; + + + + + +/*************************************************************************** + + * Public member variables + + ***************************************************************************/ + +/** + + * Max size of the local cache. Set to 0 to turn off caching. Caching is + + * useful to reduce the number of server connections. Recommended only for data + + * sources that return comprehensive results for queries or when stale data is + + * not an issue. Default: 15. + + * + + * @type number + + */ + +YAHOO.widget.DataSource.prototype.maxCacheEntries = 15; + + + +/** + + * Use this to equate cache matching with the type of matching done by your live + + * data source. If caching is on and queryMatchContains is true, the cache + + * returns results that "contain" the query string. By default, + + * queryMatchContains is set to false, meaning the cache only returns results + + * that "start with" the query string. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.DataSource.prototype.queryMatchContains = false; + + + +/** + + * Data source query subset matching. If caching is on and queryMatchSubset is + + * true, substrings of queries will return matching cached results. For + + * instance, if the first query is for "abc" susequent queries that start with + + * "abc", like "abcd", will be queried against the cache, and not the live data + + * source. Recommended only for data sources that return comprehensive results + + * for queries with very few characters. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.DataSource.prototype.queryMatchSubset = false; + + + +/** + + * Data source query case-sensitivity matching. If caching is on and + + * queryMatchCase is true, queries will only return results for case-sensitive + + * matches. Default: false. + + * + + * @type boolean + + */ + +YAHOO.widget.DataSource.prototype.queryMatchCase = false; + + + + + +/*************************************************************************** + + * Public methods + + ***************************************************************************/ + + /** + + * Public accessor to the unique name of the data source instance. + + * + + * @return {string} Unique name of the data source instance + + */ + +YAHOO.widget.DataSource.prototype.getName = function() { + + return this._sName; + +}; + + + + /** + + * Public accessor to the unique name of the data source instance. + + * + + * @return {string} Unique name of the data source instance + + */ + +YAHOO.widget.DataSource.prototype.toString = function() { + + return "DataSource " + this._sName; + +}; + + + +/** + + * Retrieves query results, first checking the local cache, then making the + + * query request to the live data source as defined by the function doQuery. + + * + + * @param {object} oCallbackFn Callback function defined by oParent object to + + * which to return results + + * @param {string} sQuery Query string + + * @param {object} oParent The object instance that has requested data + + */ + +YAHOO.widget.DataSource.prototype.getResults = function(oCallbackFn, sQuery, oParent) { + + + + // First look in cache + + var aResults = this._doQueryCache(oCallbackFn,sQuery,oParent); + + + + // Not in cache, so get results from server + + if(aResults.length === 0) { + + this.queryEvent.fire(this, oParent, sQuery); + + this.doQuery(oCallbackFn, sQuery, oParent); + + } + +}; + + + +/** + + * Abstract method implemented by subclasses to make a query to the live data + + * source. Must call the callback function with the response returned from the + + * query. Populates cache (if enabled). + + * + + * @param {object} oCallbackFn Callback function implemented by oParent to + + * which to return results + + * @param {string} sQuery Query string + + * @param {object} oParent The object instance that has requested data + + */ + +YAHOO.widget.DataSource.prototype.doQuery = function(oCallbackFn, sQuery, oParent) { + + /* override this */ + +}; + + + +/** + + * Flushes cache. + + */ + +YAHOO.widget.DataSource.prototype.flushCache = function() { + + if(this._aCache) { + + this._aCache = []; + + } + + if(this._aCacheHelper) { + + this._aCacheHelper = []; + + } + + this.cacheFlushEvent.fire(this); + +}; + + + +/*************************************************************************** + + * Events + + ***************************************************************************/ + +/** + + * Fired when a query is made to the live data source. Subscribers receive the + + * following array:
+ + * - args[0] The data source instance + + * - args[1] The requesting object + + * - args[2] The query string + + */ + +YAHOO.widget.DataSource.prototype.queryEvent = null; + + + +/** + + * Fired when a query is made to the local cache. Subscribers receive the + + * following array:
+ + * - args[0] The data source instance + + * - args[1] The requesting object + + * - args[2] The query string + + */ + +YAHOO.widget.DataSource.prototype.cacheQueryEvent = null; + + + +/** + + * Fired when data is retrieved from the live data source. Subscribers receive + + * the following array:
+ + * - args[0] The data source instance + + * - args[1] The requesting object + + * - args[2] The query string + + * - args[3] Array of result objects + + */ + +YAHOO.widget.DataSource.prototype.getResultsEvent = null; + + + +/** + + * Fired when data is retrieved from the local cache. Subscribers receive the + + * following array :
+ + * - args[0] The data source instance + + * - args[1] The requesting object + + * - args[2] The query string + + * - args[3] Array of result objects + + */ + +YAHOO.widget.DataSource.prototype.getCachedResultsEvent = null; + + + +/** + + * Fired when an error is encountered with the live data source. Subscribers + + * receive the following array:
+ + * - args[0] The data source instance + + * - args[1] The requesting object + + * - args[2] The query string + + * - args[3] Error message string + + */ + +YAHOO.widget.DataSource.prototype.dataErrorEvent = null; + + + +/** + + * Fired when the local cache is flushed. Subscribers receive the following + + * array :
+ + * - args[0] The data source instance + + */ + +YAHOO.widget.DataSource.prototype.cacheFlushEvent = null; + + + +/*************************************************************************** + + * Private member variables + + ***************************************************************************/ + +/** + + * Internal class variable to index multiple data source instances. + + * + + * @type number + + * @private + + */ + +YAHOO.widget.DataSource._nIndex = 0; + + + +/** + + * Name of data source instance. + + * + + * @type string + + * @private + + */ + +YAHOO.widget.DataSource.prototype._sName = null; + + + +/** + + * Local cache of data result objects indexed chronologically. + + * + + * @type array + + * @private + + */ + +YAHOO.widget.DataSource.prototype._aCache = null; + + + + + +/*************************************************************************** + + * Private methods + + ***************************************************************************/ + +/** + + * Initializes data source instance. + + * + + * @private + + */ + +YAHOO.widget.DataSource.prototype._init = function() { + + // Validate and initialize public configs + + var maxCacheEntries = this.maxCacheEntries; + + if(isNaN(maxCacheEntries) || (maxCacheEntries < 0)) { + + maxCacheEntries = 0; + + } + + // Initialize local cache + + if(maxCacheEntries > 0 && !this._aCache) { + + this._aCache = []; + + } + + + + this._sName = "instance" + YAHOO.widget.DataSource._nIndex; + + YAHOO.widget.DataSource._nIndex++; + + + + this.queryEvent = new YAHOO.util.CustomEvent("query", this); + + this.cacheQueryEvent = new YAHOO.util.CustomEvent("cacheQuery", this); + + this.getResultsEvent = new YAHOO.util.CustomEvent("getResults", this); + + this.getCachedResultsEvent = new YAHOO.util.CustomEvent("getCachedResults", this); + + this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this); + + this.cacheFlushEvent = new YAHOO.util.CustomEvent("cacheFlush", this); + +}; + + + +/** + + * Adds a result object to the local cache, evicting the oldest element if the + + * cache is full. Newer items will have higher indexes, the oldest item will have + + * index of 0. + + * + + * @param {object} resultObj Object literal of data results, including internal + + * properties and an array of result objects + + * @private + + */ + +YAHOO.widget.DataSource.prototype._addCacheElem = function(resultObj) { + + var aCache = this._aCache; + + // Don't add if anything important is missing. + + if(!aCache || !resultObj || !resultObj.query || !resultObj.results) { + + return; + + } + + + + // If the cache is full, make room by removing from index=0 + + if(aCache.length >= this.maxCacheEntries) { + + aCache.shift(); + + } + + + + // Add to cache, at the end of the array + + aCache.push(resultObj); + +}; + + + +/** + + * Queries the local cache for results. If query has been cached, the callback + + * function is called with the results, and the cached is refreshed so that it + + * is now the newest element. + + * + + * @param {object} oCallbackFn Callback function defined by oParent object to + + * which to return results + + * @param {string} sQuery Query string + + * @param {object} oParent The object instance that has requested data + + * @return {array} aResults Result object from local cache if found, otherwise + + * null + + * @private + + */ + +YAHOO.widget.DataSource.prototype._doQueryCache = function(oCallbackFn, sQuery, oParent) { + + var aResults = []; + + var bMatchFound = false; + + var aCache = this._aCache; + + var nCacheLength = (aCache) ? aCache.length : 0; + + var bMatchContains = this.queryMatchContains; + + + + // If cache is enabled... + + if((this.maxCacheEntries > 0) && aCache && (nCacheLength > 0)) { + + this.cacheQueryEvent.fire(this, oParent, sQuery); + + // If case is unimportant, normalize query now instead of in loops + + if(!this.queryMatchCase) { + + var sOrigQuery = sQuery; + + sQuery = sQuery.toLowerCase(); + + } + + + + // Loop through each cached element's query property... + + for(var i = nCacheLength-1; i >= 0; i--) { + + var resultObj = aCache[i]; + + var aAllResultItems = resultObj.results; + + // If case is unimportant, normalize match key for comparison + + var matchKey = (!this.queryMatchCase) ? + + encodeURIComponent(resultObj.query.toLowerCase()): + + encodeURIComponent(resultObj.query); + + + + // If a cached match key exactly matches the query... + + if(matchKey == sQuery) { + + // Stash all result objects into aResult[] and stop looping through the cache. + + bMatchFound = true; + + aResults = aAllResultItems; + + + + // The matching cache element was not the most recent, + + // so now we need to refresh the cache. + + if(i != nCacheLength-1) { + + // Remove element from its original location + + aCache.splice(i,1); + + // Add element as newest + + this._addCacheElem(resultObj); + + } + + break; + + } + + // Else if this query is not an exact match and subset matching is enabled... + + else if(this.queryMatchSubset) { + + // Loop through substrings of each cached element's query property... + + for(var j = sQuery.length-1; j >= 0 ; j--) { + + var subQuery = sQuery.substr(0,j); + + + + // If a substring of a cached sQuery exactly matches the query... + + if(matchKey == subQuery) { + + bMatchFound = true; + + + + // Go through each cached result object to match against the query... + + for(var k = aAllResultItems.length-1; k >= 0; k--) { + + var aRecord = aAllResultItems[k]; + + var sKeyIndex = (this.queryMatchCase) ? + + encodeURIComponent(aRecord[0]).indexOf(sQuery): + + encodeURIComponent(aRecord[0]).toLowerCase().indexOf(sQuery); + + + + // A STARTSWITH match is when the query is found at the beginning of the key string... + + if((!bMatchContains && (sKeyIndex === 0)) || + + // A CONTAINS match is when the query is found anywhere within the key string... + + (bMatchContains && (sKeyIndex > -1))) { + + // Stash a match into aResults[]. + + aResults.unshift(aRecord); + + } + + } + + + + // Add the subset match result set object as the newest element to cache, + + // and stop looping through the cache. + + resultObj = {}; + + resultObj.query = sQuery; + + resultObj.results = aResults; + + this._addCacheElem(resultObj); + + break; + + } + + } + + if(bMatchFound) { + + break; + + } + + } + + } + + + + // If there was a match, send along the results. + + if(bMatchFound) { + + this.getCachedResultsEvent.fire(this, oParent, sOrigQuery, aResults); + + oCallbackFn(sOrigQuery, aResults, oParent); + + } + + } + + return aResults; + +}; + + + + + +/****************************************************************************/ + +/****************************************************************************/ + +/****************************************************************************/ + + + +/** + + * Implementation of YAHOO.widget.DataSource using XML HTTP requests that return + + * query results. + + * requires YAHOO.util.Connect XMLHTTPRequest library + + * extends YAHOO.widget.DataSource + + * + + * @constructor + + * @param {string} sScriptURI Absolute or relative URI to script that returns + + * query results as JSON, XML, or delimited flat data + + * @param {array} aSchema Data schema definition of results + + * @param {object} oConfigs Optional object literal of config params + + */ + +YAHOO.widget.DS_XHR = function(sScriptURI, aSchema, oConfigs) { + + // Set any config params passed in to override defaults + + if(typeof oConfigs == "object") { + + for(var sConfig in oConfigs) { + + this[sConfig] = oConfigs[sConfig]; + + } + + } + + + + // Initialization sequence + + if(!aSchema || (aSchema.constructor != Array)) { + + YAHOO.log("Could not instantiate XHR DataSource due to invalid arguments", "error", this.toString()); + + return; + + } + + else { + + this.schema = aSchema; + + } + + this.scriptURI = sScriptURI; + + this._init(); + + YAHOO.log("XHR DataSource initialized","info",this.toString()); + +}; + + + +YAHOO.widget.DS_XHR.prototype = new YAHOO.widget.DataSource(); + + + +/*************************************************************************** + + * Public constants + + ***************************************************************************/ + +/** + + * JSON data type + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DS_XHR.prototype.TYPE_JSON = 0; + + + +/** + + * XML data type + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DS_XHR.prototype.TYPE_XML = 1; + + + +/** + + * Flat file data type + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DS_XHR.prototype.TYPE_FLAT = 2; + + + +/** + + * Error message for XHR failure. + + * + + * @type constant + + * @final + + */ + +YAHOO.widget.DS_XHR.prototype.ERROR_DATAXHR = "XHR response failed"; + + + +/*************************************************************************** + + * Public member variables + + ***************************************************************************/ + +/** + + * Number of milliseconds the XHR connection will wait for a server response. A + + * a value of zero indicates the XHR connection will wait forever. Any value + + * greater than zero will use the Connection utility's Auto-Abort feature. + + * Default: 0. + + * + + * @type number + + */ + +YAHOO.widget.DS_XHR.prototype.connTimeout = 0; + + + + + +/** + + * Absolute or relative URI to script that returns query results. For instance, + + * queries will be sent to + + * ?=userinput + + * + + * @type string + + */ + +YAHOO.widget.DS_XHR.prototype.scriptURI = null; + + + +/** + + * Query string parameter name sent to scriptURI. For instance, queries will be + + * sent to + + * ?=userinput + + * Default: "query". + + * + + * @type string + + */ + +YAHOO.widget.DS_XHR.prototype.scriptQueryParam = "query"; + + + +/** + + * String of key/value pairs to append to requests made to scriptURI. Define + + * this string when you want to send additional query parameters to your script. + + * When defined, queries will be sent to + + * ?=userinput& + + * Default: "". + + * + + * @type string + + */ + +YAHOO.widget.DS_XHR.prototype.scriptQueryAppend = ""; + + + +/** + + * XHR response data type. Other types that may be defined are TYPE_XML and + + * TYPE_FLAT. Default: TYPE_JSON. + + * + + * @type type + + */ + +YAHOO.widget.DS_XHR.prototype.responseType = YAHOO.widget.DS_XHR.prototype.TYPE_JSON; + + + +/** + + * String after which to strip results. If the results from the XHR are sent + + * back as HTML, the gzip HTML comment appears at the end of the data and should + + * be ignored. Default: "\n<!--" + + * + + * @type string + + */ + +YAHOO.widget.DS_XHR.prototype.responseStripAfter = "\n